build concerns, build matrix

This commit is contained in:
2026-08-04 08:37:49 +02:00
parent f943357ebd
commit 77f269d9e0
36 changed files with 602 additions and 57 deletions
+9
View File
@@ -0,0 +1,9 @@
import type { BuildsData } from '../lib/interfaces/builds.interface'
const index = async (): Promise<BuildsData> => {
const res = await fetch('/api/builds')
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return res.json()
}
export default { index }
+4
View File
@@ -179,6 +179,10 @@ export default {
new: 'New',
read: 'Read',
},
builds: {
title: 'Build Matrix',
subtitle: 'Which engines build for which platforms.',
},
code: {
title: 'Codebase',
subtitle: 'Explore our collection of open-source projects, study our source code, and contribute to our independent game development tools.',
+4
View File
@@ -179,6 +179,10 @@ export default {
new: 'Új',
read: 'Olvasás',
},
builds: {
title: 'Build mátrix',
subtitle: 'Melyik engine melyik platformra fordít.',
},
code: {
title: 'Kódbázis',
subtitle: 'Fedezd fel nyílt forráskódú projektek gyűjteményét, tanulmányozd forráskódunkat, és járulj hozzá független játékfejlesztő eszközeinkhez.',
@@ -0,0 +1,9 @@
export interface PlatformBuilds {
label: string
kinds: string[]
}
export interface BuildsData {
platforms: Record<string, PlatformBuilds>
allKinds: string[]
}
@@ -0,0 +1,116 @@
<template>
<header class="hero-section-gradient from-slate-700 to-gray-800 py-16">
<div class="hero-container">
<h1 class="hero-title">{{ t('builds.title') }}</h1>
<p class="hero-subtitle text-slate-300">{{ t('builds.subtitle') }}</p>
</div>
</header>
<main class="main-container py-12 px-4 mt-0">
<div v-if="error" class="builds-error">{{ error }}</div>
<div v-else-if="!data" class="builds-loading">{{ t('common.loading') }}</div>
<div v-else class="builds-table-wrap">
<table class="builds-table">
<thead>
<tr>
<th class="builds-th-engine">Engine</th>
<th v-for="kind in data.allKinds" :key="kind" class="builds-th-kind">
<i :class="kindIcon(kind)" class="builds-kind-icon"></i>
<span class="builds-kind-label">{{ t('catalogShow.assetKind.' + kind) }}</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(info, platform) in data.platforms" :key="platform" class="builds-row">
<td class="builds-td-engine">{{ info.label }}</td>
<td v-for="kind in data.allKinds" :key="kind" class="builds-td-kind">
<i v-if="info.kinds.includes(kind)" class="fa-solid fa-check builds-check"></i>
<i v-else class="fa-solid fa-xmark builds-xmark"></i>
</td>
</tr>
</tbody>
</table>
</div>
<div class="builds-back">
<RouterLink to="/catalog" class="builds-back-link">
{{ t('catalogShow.back') }}
</RouterLink>
</div>
</main>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { RouterLink } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useBuildsStore } from '../../stores/builds.store'
const { t } = useI18n()
const store = useBuildsStore()
const { data, error } = storeToRefs(store)
const kindIcon = (kind: string) =>
kind.startsWith('win_') ? 'fa-brands fa-windows'
: kind.startsWith('linux_') ? 'fa-brands fa-linux'
: kind.startsWith('mac_') ? 'fa-brands fa-apple'
: kind === 'cartridge' ? 'fa-solid fa-gamepad'
: kind === 'source' ? 'fa-solid fa-code'
: kind === 'docs' ? 'fa-solid fa-book'
: kind === 'html' ? 'fa-solid fa-globe'
: 'fa-solid fa-cube'
onMounted(() => store.fetch())
</script>
<style scoped>
.builds-error {
@apply text-center text-red-500 font-bold py-12;
}
.builds-loading {
@apply text-center text-gray-400 py-12;
}
.builds-table-wrap {
@apply bg-white rounded-2xl shadow-xl border border-gray-100 overflow-x-auto;
}
.builds-table {
@apply w-full text-sm;
}
.builds-th-engine {
@apply px-6 py-4 text-left text-xs font-bold text-gray-400 uppercase tracking-widest bg-gray-50;
}
.builds-th-kind {
@apply px-3 py-4 text-center bg-gray-50;
}
.builds-kind-icon {
@apply block text-gray-500 text-base mb-1;
}
.builds-kind-label {
@apply block text-[10px] font-bold text-gray-400 uppercase tracking-wider leading-tight;
}
.builds-row {
@apply border-t border-gray-100 hover:bg-gray-50/50 transition-colors;
}
.builds-td-engine {
@apply px-6 py-4 font-bold text-gray-900 whitespace-nowrap;
}
.builds-td-kind {
@apply px-3 py-4 text-center;
}
.builds-check {
@apply text-green-500 text-lg;
}
.builds-xmark {
@apply text-gray-200 text-lg;
}
.builds-back {
@apply mt-8 text-center;
}
.builds-back-link {
@apply text-gray-400 hover:text-gray-600 text-sm font-medium transition-colors;
}
</style>
@@ -18,6 +18,12 @@
</button>
</div>
<div class="builds-link-row">
<RouterLink to="/builds" class="builds-link">
<i class="fa-solid fa-table-cells"></i> {{ t('builds.title') }}
</RouterLink>
</div>
<div class="software-list">
<template v-for="{ software, releases } in softwares" :key="software.name">
<div v-show="activeFilter === 'all' || software.status === activeFilter" class="software-card group">
@@ -152,4 +158,10 @@ onMounted(() => store.fetchAll())
.software-download-count {
@apply text-xs text-gray-400 font-medium ml-1;
}
.builds-link-row {
@apply text-center mb-6;
}
.builds-link {
@apply text-sm text-gray-400 hover:text-purple-600 font-medium transition-colors;
}
</style>
@@ -0,0 +1,5 @@
import type { RouteRecordRaw } from 'vue-router'
export const buildsRouter: RouteRecordRaw[] = [
{ path: '/builds', name: 'buildsIndex', component: () => import('../page/builds/BuildsIndexPage.vue') },
]
+2
View File
@@ -6,6 +6,7 @@ import { codeRouter } from './code.router'
import { contactRouter } from './contact.router'
import { howtosRouter } from './howtos.router'
import { teamRouter } from './team.router'
import { buildsRouter } from './builds.router'
export const router = createRouter({
history: createWebHistory(),
@@ -17,6 +18,7 @@ export const router = createRouter({
...contactRouter,
...howtosRouter,
...teamRouter,
...buildsRouter,
],
scrollBehavior() {
return { top: 0 }
+18
View File
@@ -0,0 +1,18 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import buildsApi from '../api/builds.api'
import { useLoadable } from '../composables/useLoadable'
import type { BuildsData } from '../lib/interfaces/builds.interface'
export const useBuildsStore = defineStore('builds', () => {
const data = ref<BuildsData | null>(null)
const { loading, error, withCache, invalidate } = useLoadable()
async function fetch() {
await withCache(async () => {
data.value = await buildsApi.index()
})
}
return { data, loading, error, fetch, invalidate }
})