ssr
Gitea Actions Demo / build_and_push (push) Successful in 1m13s Details

This commit is contained in:
artem 2026-09-19 00:40:05 +03:00
parent dde4aea2e8
commit 1fc820e08b
1 changed files with 145 additions and 0 deletions

View File

@ -0,0 +1,145 @@
<template>
<section class="landing">
<h1>Cycle Rider — анализ велотренировок</h1>
<p class="landing-description">
Платформа для анализа велотренировок: мощность, пульс, скорость, каденс.
Загрузите FIT/GPX файлы и получите детальную аналитику.
</p>
<h2>Последние тренировки</h2>
<ul class="landing-workouts" v-if="workouts.length">
<li v-for="w in workouts" :key="w.id">
<a :href="`/public/workouts/${w.id}`">{{ w.name }}</a>
<span class="landing-meta">
— {{ formatDate(w.workouted_at) }},
{{ Math.round(w.distantion / 1000) }} км
</span>
</li>
</ul>
<p v-else>Загрузка...</p>
<h2>Возможности</h2>
<ul class="landing-features">
<li>Нормализованная мощность (NP) и FTP</li>
<li>Пульсовые зоны и скорость</li>
<li>Каденс и дистанция</li>
<li>Профиль высоты</li>
<li>Загрузка FIT/GPX</li>
</ul>
<div class="landing-cta">
<a href="/explore" class="btn-primary">Смотреть все тренировки →</a>
<a href="/auth/signup" class="btn-secondary">Регистрация</a>
</div>
</section>
</template>
<script setup lang="ts">
import { ref, inject, onMounted } from "vue";
import { useRouter } from "vue-router";
import { AxiosInstance } from "axios";
const router = useRouter();
const axiosPublic = inject("axiosPublic") as AxiosInstance;
interface LandingWorkout {
id: string;
name: string;
workouted_at: string;
distantion: number;
}
const workouts = ref<LandingWorkout[]>([]);
const formatDate = (iso: string): string => {
const d = new Date(iso);
const dd = String(d.getDate()).padStart(2, "0");
const mm = String(d.getMonth() + 1).padStart(2, "0");
const yyyy = d.getFullYear();
return `${dd}.${mm}.${yyyy}`;
};
onMounted(() => {
// Authenticated → redirect to explore
if (typeof window !== "undefined" && localStorage.getItem("token")) {
router.push({ name: "explore" });
return;
}
// Unauthenticated → fetch top 5 workouts for display
if (axiosPublic) {
axiosPublic
.get<{ results: LandingWorkout[] }>("/api/v0/public/workouts")
.then((res) => {
workouts.value = res.data.results.slice(0, 5);
})
.catch(() => {});
}
});
</script>
<style scoped>
.landing {
max-width: 800px;
margin: 0 auto;
padding: 2rem 1rem;
text-align: center;
}
.landing h1 {
font-size: 2rem;
margin-bottom: 1rem;
}
.landing-description {
color: var(--va-secondary, #666);
font-size: 1.1rem;
margin-bottom: 2rem;
}
.landing-workouts {
list-style: none;
padding: 0;
margin: 1rem 0 2rem;
text-align: left;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
.landing-workouts li {
padding: 0.5rem 0;
border-bottom: 1px solid var(--va-border-color, #eee);
}
.landing-meta {
color: var(--va-secondary, #999);
font-size: 0.9rem;
}
.landing-features {
list-style: none;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1.5rem;
justify-content: center;
margin: 1rem 0 2rem;
}
.landing-cta {
display: flex;
gap: 1rem;
justify-content: center;
margin-top: 2rem;
}
.btn-primary {
display: inline-block;
padding: 0.75rem 1.5rem;
background: var(--va-primary, #007aff);
color: white;
text-decoration: none;
border-radius: 8px;
}
.btn-secondary {
display: inline-block;
padding: 0.75rem 1.5rem;
border: 1px solid var(--va-border-color, #ccc);
color: var(--va-primary-25, #333);
text-decoration: none;
border-radius: 8px;
}
</style>