Lint frontend
Some checks failed
pipeline / lint-and-format (push) Failing after 4m18s
pipeline / build-and-push-images (push) Has been skipped

This commit is contained in:
2026-01-21 23:39:03 +01:00
parent f307e3a8fc
commit 3d79d9e495
21 changed files with 356 additions and 183 deletions

View File

@@ -32,8 +32,8 @@ const champions = computed(() => {
return championsData.value
.slice(1)
.filter((champion: any) => !champion.name.includes('Doom Bot'))
.sort((a: any, b: any) => a.name.localeCompare(b.name))
.filter((champion: ChampionSummary) => !champion.name.includes('Doom Bot'))
.sort((a: ChampionSummary, b: ChampionSummary) => a.name.localeCompare(b.name))
})
const lanesMap = computed(() => {
@@ -64,7 +64,7 @@ function filterChampionsByLane(laneFilter: number): void {
}
const laneName = filterToLane(laneFilter)
filteredChampions.value = champions.value.filter((champion: any) => {
filteredChampions.value = champions.value.filter((champion: ChampionSummary) => {
const championLanes = lanesMap.value.get(champion.alias.toLowerCase())
if (!championLanes) return false
@@ -77,14 +77,14 @@ const debouncedSearch = debounce((searchTerm: string) => {
if (isEmpty(searchTerm)) {
filteredChampions.value = [...champions.value]
} else {
filteredChampions.value = champions.value.filter((champion: any) =>
filteredChampions.value = champions.value.filter((champion: ChampionSummary) =>
champion.name.toLowerCase().includes(searchTerm.toLowerCase())
)
}
}, 300)
// Watchers
watch(searchBar, (newS, oldS) => {
watch(searchBar, (_newS, _oldS) => {
searchBar.value?.focus()
})
@@ -124,7 +124,7 @@ const isLoading = computed(() => loadingChampions.value || loadingLanes.value)
<div>
<!-- Loading state -->
<div v-if="isLoading" class="loading-state">
<div class="loading-spinner"/>
<div class="loading-spinner" />
<p>Loading champions...</p>
</div>
@@ -146,7 +146,7 @@ const isLoading = computed(() => loadingChampions.value || loadingLanes.value)
@keyup.enter="
() => filteredChampions.length > 0 && navigateToChampion(filteredChampions[0].alias)
"
>
/>
</div>
<!-- Empty state -->

View File

@@ -46,6 +46,7 @@ function handleHover(laneImg: Ref<string>, index: number) {
<div style="width: fit-content">
<NuxtImg
v-for="(laneImg, index) in laneImgs"
:key="index"
format="webp"
:alt="POSITIONS_STR[index]"
class="lane-img"

View File

@@ -3,6 +3,11 @@ defineProps<{
imgWidth?: string
fontSize?: string
}>()
// Component name must be multi-word
defineOptions({
name: 'AppLogo'
})
</script>
<template>

View File

@@ -108,14 +108,18 @@ function handleRefresh() {
height="64"
:alt="tree.data.toString()"
:src="CDRAGON_BASE + mapPath(itemMap.get(tree.data).iconPath)"
>
/>
<h3 style="width: fit-content; margin: auto; margin-bottom: 10px">
{{ ((tree.count / parentCount!!) * 100).toFixed(0) }}%
</h3>
</div>
<div style="margin-left: 30px">
<div v-for="child in tree.children" style="width: fit-content; height: fit-content">
<div
v-for="child in tree.children"
:key="child.data"
style="width: fit-content; height: fit-content"
>
<ItemTree
:tree="child"
:parent-count="tree.count"

View File

@@ -12,7 +12,7 @@ const ITEMS_API_URL = CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/defaul
// State
const { data: items, pending: loadingItems, error: itemsError } = await useFetch(ITEMS_API_URL)
const itemMap = ref<Map<number, any>>(new Map())
const itemMap = ref<Map<number, unknown>>(new Map())
// Initialize item map
watch(
@@ -21,7 +21,7 @@ watch(
try {
const itemsData = newItems || []
if (Array.isArray(itemsData)) {
const map = new Map<number, any>()
const map = new Map<number, unknown>()
for (const item of itemsData) {
if (item?.id) {
map.set(item.id, item)
@@ -90,24 +90,9 @@ function trimLateGameItems(builds: Builds): void {
trimLateGameItemsFromTree(builds.tree)
}
/**
* Get item data safely
*/
function getItemData(itemId: number): any {
return itemMap.value.get(itemId) || { iconPath: '' }
}
/**
* Calculate percentage for item display
*/
function getItemPercentage(item: { count: number }, total: number): string {
if (total <= 0) return '0%'
return ((item.count / total) * 100).toFixed(0) + '%'
}
// Error and loading states
const hasError = computed(() => itemsError.value || props.error)
const isLoading = computed(() => loadingItems.value || props.loading)
const _hasError = computed(() => itemsError.value || props.error)
const _isLoading = computed(() => loadingItems.value || props.loading)
</script>
<template>
@@ -116,14 +101,18 @@ const isLoading = computed(() => loadingItems.value || props.loading)
<!-- Start items -->
<ItemBox v-if="builds.suppItems == undefined || builds.suppItems == null" title="start">
<div class="iv-items-container">
<div v-for="item in builds.start" style="margin-left: 5px; margin-right: 5px">
<div
v-for="item in builds.start"
:key="item.data"
style="margin-left: 5px; margin-right: 5px"
>
<NuxtImg
v-if="item.data != null && item.data != undefined"
class="item-img"
width="64"
height="64"
:alt="item.data.toString()"
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)"
:src="CDRAGON_BASE + mapPath((itemMap.get(item.data) as any).iconPath)"
/>
<h3 style="width: fit-content; margin: auto; margin-bottom: 10px">
{{ ((item.count / builds.tree.count) * 100).toFixed(0) }}%
@@ -134,14 +123,18 @@ const isLoading = computed(() => loadingItems.value || props.loading)
<!-- Supp items -->
<ItemBox v-if="builds.suppItems != undefined && builds.suppItems != null" title="supp">
<div class="iv-items-container">
<div v-for="item in builds.suppItems" style="margin-left: 5px; margin-right: 5px">
<div
v-for="item in builds.suppItems"
:key="item.data"
style="margin-left: 5px; margin-right: 5px"
>
<NuxtImg
v-if="item.data != null && item.data != undefined"
class="item-img"
width="64"
height="64"
:alt="item.data.toString()"
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)"
:src="CDRAGON_BASE + mapPath((itemMap.get(item.data) as any).iconPath)"
/>
<h3 style="width: fit-content; margin: auto; margin-bottom: 10px">
{{ ((item.count / builds.tree.count) * 100).toFixed(0) }}%
@@ -154,14 +147,18 @@ const isLoading = computed(() => loadingItems.value || props.loading)
<!-- Boots first : when champion rush boots -->
<ItemBox v-if="builds.bootsFirst > 0.5" title="boots rush" :boots-first="builds.bootsFirst">
<div class="iv-items-container">
<div v-for="item in builds.boots" style="margin-left: 5px; margin-right: 5px">
<div
v-for="item in builds.boots"
:key="item.data"
style="margin-left: 5px; margin-right: 5px"
>
<NuxtImg
v-if="item.data != null && item.data != undefined"
class="item-img"
width="64"
height="64"
:alt="item.data.toString()"
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)"
:src="CDRAGON_BASE + mapPath((itemMap.get(item.data) as any).iconPath)"
/>
<h3 style="width: fit-content; margin: auto; margin-bottom: 10px">
{{ ((item.count / builds.tree.count) * 100).toFixed(0) }}%
@@ -178,14 +175,18 @@ const isLoading = computed(() => loadingItems.value || props.loading)
<!-- Boots -->
<ItemBox v-if="builds.bootsFirst <= 0.5" title="boots">
<div class="iv-items-container">
<div v-for="item in builds.boots.slice(0, 4)" style="margin-left: 5px; margin-right: 5px">
<div
v-for="item in builds.boots.slice(0, 4)"
:key="item.data"
style="margin-left: 5px; margin-right: 5px"
>
<NuxtImg
v-if="item.data != null && item.data != undefined"
class="item-img"
width="64"
height="64"
:alt="item.data.toString()"
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)"
:src="CDRAGON_BASE + mapPath((itemMap.get(item.data) as any).iconPath)"
/>
<h3 style="width: fit-content; margin: auto; margin-bottom: 10px">
{{ ((item.count / builds.tree.count) * 100).toFixed(0) }}%
@@ -200,6 +201,7 @@ const isLoading = computed(() => loadingItems.value || props.loading)
<div class="iv-items-container">
<div
v-for="item in builds.lateGame.slice(0, 4)"
:key="item.data"
style="margin-left: 5px; margin-right: 5px"
>
<NuxtImg
@@ -208,7 +210,7 @@ const isLoading = computed(() => loadingItems.value || props.loading)
width="64"
height="64"
:alt="item.data.toString()"
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)"
:src="CDRAGON_BASE + mapPath((itemMap.get(item.data) as any).iconPath)"
/>
<h3 style="width: fit-content; margin: auto; margin-bottom: 10px">
{{ ((item.count / builds.tree.count) * 100).toFixed(0) }}%
@@ -219,6 +221,7 @@ const isLoading = computed(() => loadingItems.value || props.loading)
<div v-if="builds.lateGame.length > 4" class="iv-items-container">
<div
v-for="item in builds.lateGame.slice(4, 8)"
:key="item.data"
style="margin-left: 5px; margin-right: 5px"
>
<NuxtImg
@@ -227,7 +230,7 @@ const isLoading = computed(() => loadingItems.value || props.loading)
width="64"
height="64"
:alt="item.data.toString()"
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)"
:src="CDRAGON_BASE + mapPath((itemMap.get(item.data) as any).iconPath)"
/>
<h3 style="width: fit-content; margin: auto; margin-bottom: 10px">
{{ ((item.count / builds.tree.count) * 100).toFixed(0) }}%

View File

@@ -49,6 +49,7 @@ if (route.path.startsWith('/tierlist/')) {
<div
v-for="(lane, i) in championLanes"
:key="i"
style="display: flex; align-items: center; margin-left: 20px"
>
<NuxtImg
@@ -82,6 +83,7 @@ if (route.path.startsWith('/tierlist/')) {
<div style="display: flex">
<NuxtLink
v-for="(pos, i) in POSITIONS"
:key="i"
style="margin-top: 5px; margin-bottom: 5px"
:to="'/tierlist/' + pos"
>

View File

@@ -33,7 +33,7 @@ if (route.path.startsWith('/tierlist/')) {
<template>
<!-- To make content have a 300px margin -->
<div class="sidebar-margin"/>
<div class="sidebar-margin" />
<div class="sidebar-container">
<Logo
@@ -42,7 +42,7 @@ if (route.path.startsWith('/tierlist/')) {
style="padding-left: 15px; padding-right: 15px; margin-top: 30px"
/>
<div v-for="(lane, i) in championLanes">
<div v-for="(lane, i) in championLanes" :key="i">
<div
style="
display: flex;
@@ -103,6 +103,7 @@ if (route.path.startsWith('/tierlist/')) {
<h2 style="padding-left: 20px; font-size: 2.4rem; margin-bottom: 10px">Tierlist</h2>
<NuxtLink
v-for="(pos, i) in POSITIONS"
:key="i"
style="margin-top: 5px; margin-bottom: 5px"
:to="'/tierlist/' + pos"
>

View File

@@ -21,13 +21,13 @@ const { data: stylesData }: PerkStylesResponse = await useFetch(
)
watch(
() => props.primaryStyleId,
async (newP, oldP) => {
async (_newP, _oldP) => {
refreshStyles()
}
)
watch(
() => props.secondaryStyleId,
async (newP, oldP) => {
async (_newP, _oldP) => {
refreshStyles()
}
)
@@ -55,9 +55,14 @@ refreshStyles()
:src="CDRAGON_BASE + mapPath(primaryStyle.iconPath)"
/>
</div>
<div v-for="slot in primaryStyle.slots.slice(0, 1)" class="rune-slot">
<div
v-for="(slot, slotIndex) in primaryStyle.slots.slice(0, 1)"
:key="slotIndex"
class="rune-slot"
>
<NuxtImg
v-for="perk in slot.perks"
:key="perk"
width="48"
:class="
'rune-img rune-keystone ' + (props.selectionIds.includes(perk) ? 'rune-activated' : '')
@@ -65,23 +70,33 @@ refreshStyles()
:src="'https://raw.communitydragon.org/latest/' + mapPath(perks.get(perk).iconPath)"
/>
</div>
<div v-for="slot in primaryStyle.slots.slice(1, 4)" class="rune-slot">
<div
v-for="(slot, slotIndex) in primaryStyle.slots.slice(1, 4)"
:key="slotIndex"
class="rune-slot"
>
<NuxtImg
v-for="perk in slot.perks"
:key="perk"
width="48"
:class="'rune-img ' + (props.selectionIds.includes(perk) ? 'rune-activated' : '')"
:src="'https://raw.communitydragon.org/latest/' + mapPath(perks.get(perk).iconPath)"
/>
</div>
</div>
<div class="rune-spacer-bar"/>
<div class="rune-spacer-bar" />
<div class="rune-holder" style="align-content: end">
<div class="rune-slot">
<img style="margin: auto" :src="CDRAGON_BASE + mapPath(secondaryStyle.iconPath)" >
<img style="margin: auto" :src="CDRAGON_BASE + mapPath(secondaryStyle.iconPath)" />
</div>
<div v-for="slot in secondaryStyle.slots.slice(1, 4)" class="rune-slot">
<div
v-for="(slot, slotIndex) in secondaryStyle.slots.slice(1, 4)"
:key="slotIndex"
class="rune-slot"
>
<NuxtImg
v-for="perk in slot.perks"
:key="perk"
width="48"
:class="'rune-img ' + (props.selectionIds.includes(perk) ? 'rune-activated' : '')"
:src="'https://raw.communitydragon.org/latest/' + mapPath(perks.get(perk).iconPath)"

View File

@@ -27,7 +27,7 @@ const { data: stylesData }: PerkStylesResponse = await useFetch(
)
watch(
() => props.runes,
(newRunes, oldRunes) => {
(_newRunes, _oldRunes) => {
currentlySelectedPage.value = 0
primaryStyles.value = Array(props.runes.length)
secondaryStyles.value = Array(props.runes.length)
@@ -72,7 +72,7 @@ function runeSelect(index: number) {
:selection-ids="runes[currentlySelectedPage].selections"
/>
<div style="display: flex; margin-top: 20px; justify-content: center">
<div v-for="(_, i) in runes" @click="runeSelect(i)">
<div v-for="(_, i) in runes" :key="i" @click="runeSelect(i)">
<div
:class="
'rune-selector-entry ' +

View File

@@ -6,9 +6,7 @@ import {
Legend,
BarElement,
CategoryScale,
LinearScale,
plugins,
scales
LinearScale
} from 'chart.js'
import { Bar } from 'vue-chartjs'
@@ -72,10 +70,20 @@ const chartOptions = ref({
const chartPlugins = [
{
id: 'image-draw',
afterDraw: (chart: any) => {
const ctx: CanvasRenderingContext2D = chart.ctx
const xAxis = chart.scales.x
xAxis.ticks.forEach((value: any, index: number) => {
afterDraw: (chart: unknown) => {
const ctx: CanvasRenderingContext2D = (chart as { ctx: CanvasRenderingContext2D }).ctx
const xAxis = (
chart as {
scales: {
x: {
ticks: Array<unknown>
getPixelForTick: (_index: number) => number
bottom: number
}
}
}
).scales.x
xAxis.ticks.forEach((value: unknown, index: number) => {
const x = xAxis.getPixelForTick(index)
const image = new Image()
image.src = images[index]

View File

@@ -11,6 +11,7 @@ defineProps<{
<div class="tierlist-tier-container">
<NuxtLink
v-for="{ champion: champion } in tier"
:key="champion.alias"
:to="'/champion/' + champion.alias.toLowerCase()"
>
<div class="champion-img-container">