ssr
Gitea Actions Demo / build_and_push (push) Successful in 1m13s
Details
Gitea Actions Demo / build_and_push (push) Successful in 1m13s
Details
This commit is contained in:
parent
dde4aea2e8
commit
1fc820e08b
|
|
@ -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>
|
||||
Loading…
Reference in New Issue