Add rowing to store
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import axios from "axios";
|
||||
import { ref, computed } from "vue";
|
||||
import Header from "@/components/text/Header.vue";
|
||||
import { useHomeDataStore } from "@/stores/homeData";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
const store = useHomeDataStore();
|
||||
const { loaded, error, rowingSessions } = storeToRefs(store);
|
||||
|
||||
const rows = computed(() => rowingSessions.value.slice().reverse());
|
||||
const loading = computed(() => !loaded.value);
|
||||
|
||||
const rows = ref([]);
|
||||
const loading = ref(true);
|
||||
const error = ref(null);
|
||||
const metric = ref("distance");
|
||||
const hovered = ref(null);
|
||||
|
||||
@@ -15,18 +19,9 @@ const METRICS = [
|
||||
{ key: "calories", label: "Calories", color: "#62ff57" },
|
||||
];
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await axios.get("/api/rowing");
|
||||
rows.value = res.data.slice().reverse(); // API returns DESC, reverse to chronological
|
||||
} catch (e) {
|
||||
error.value = e.message;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
const activeMetric = computed(() => METRICS.find((m) => m.key === metric.value));
|
||||
const activeMetric = computed(() =>
|
||||
METRICS.find((m) => m.key === metric.value),
|
||||
);
|
||||
|
||||
// SVG layout constants
|
||||
const W = 290;
|
||||
@@ -43,7 +38,7 @@ const chartData = computed(() =>
|
||||
date: new Date(r.date),
|
||||
value: r[metric.value],
|
||||
raw: r,
|
||||
}))
|
||||
})),
|
||||
);
|
||||
|
||||
const minVal = computed(() => Math.min(...chartData.value.map((d) => d.value)));
|
||||
@@ -64,16 +59,25 @@ const points = computed(() => {
|
||||
}));
|
||||
});
|
||||
|
||||
const polyline = computed(() => points.value.map((p) => `${p.x},${p.y}`).join(" "));
|
||||
const polyline = computed(() =>
|
||||
points.value.map((p) => `${p.x},${p.y}`).join(" "),
|
||||
);
|
||||
|
||||
const xLabels = computed(() => {
|
||||
const data = chartData.value;
|
||||
const pts = points.value;
|
||||
if (!data.length) return [];
|
||||
const indices = new Set([0, Math.floor((data.length - 1) / 2), data.length - 1]);
|
||||
const indices = new Set([
|
||||
0,
|
||||
Math.floor((data.length - 1) / 2),
|
||||
data.length - 1,
|
||||
]);
|
||||
return [...indices].map((i) => ({
|
||||
x: pts[i].x,
|
||||
label: data[i].date.toLocaleDateString("en-GB", { month: "short", day: "numeric" }),
|
||||
label: data[i].date.toLocaleDateString("en-GB", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
}),
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -194,7 +198,9 @@ function formatValue(key, val) {
|
||||
font-size="10"
|
||||
fill="var(--primary)"
|
||||
font-family="var(--font_heading)"
|
||||
>{{ yl.label }}</text>
|
||||
>
|
||||
{{ yl.label }}
|
||||
</text>
|
||||
|
||||
<!-- X axis labels -->
|
||||
<text
|
||||
@@ -206,11 +212,27 @@ function formatValue(key, val) {
|
||||
font-size="10"
|
||||
fill="var(--primary)"
|
||||
font-family="var(--font_heading)"
|
||||
>{{ xl.label }}</text>
|
||||
>
|
||||
{{ xl.label }}
|
||||
</text>
|
||||
|
||||
<!-- Axes -->
|
||||
<line :x1="PL" :y1="PT" :x2="PL" :y2="PT + PLOT_H" stroke="var(--primary)" stroke-width="0.5" />
|
||||
<line :x1="PL" :y1="PT + PLOT_H" :x2="W - PR" :y2="PT + PLOT_H" stroke="var(--primary)" stroke-width="0.5" />
|
||||
<line
|
||||
:x1="PL"
|
||||
:y1="PT"
|
||||
:x2="PL"
|
||||
:y2="PT + PLOT_H"
|
||||
stroke="var(--primary)"
|
||||
stroke-width="0.5"
|
||||
/>
|
||||
<line
|
||||
:x1="PL"
|
||||
:y1="PT + PLOT_H"
|
||||
:x2="W - PR"
|
||||
:y2="PT + PLOT_H"
|
||||
stroke="var(--primary)"
|
||||
stroke-width="0.5"
|
||||
/>
|
||||
|
||||
<!-- Tooltip -->
|
||||
<g v-if="hovered !== null && points[hovered]">
|
||||
@@ -230,14 +252,24 @@ function formatValue(key, val) {
|
||||
font-size="12"
|
||||
fill="var(--secondary)"
|
||||
font-family="var(--font_heading)"
|
||||
>{{ points[hovered].date.toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "2-digit" }) }}</text>
|
||||
>
|
||||
{{
|
||||
points[hovered].date.toLocaleDateString("en-GB", {
|
||||
day: "numeric",
|
||||
month: "short",
|
||||
year: "2-digit",
|
||||
})
|
||||
}}
|
||||
</text>
|
||||
<text
|
||||
:x="Math.min(points[hovered].x + 7, W - 82)"
|
||||
:y="points[hovered].y + 8"
|
||||
font-size="14"
|
||||
:fill="activeMetric.color"
|
||||
font-family="var(--font_heading)"
|
||||
>{{ formatValue(metric, points[hovered].value) }}</text>
|
||||
>
|
||||
{{ formatValue(metric, points[hovered].value) }}
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
@@ -246,15 +278,29 @@ function formatValue(key, val) {
|
||||
<div class="flex justify-between text-xs border-t border-quaternary pt-1">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-primary font-heading">{{ rows.length }}</span>
|
||||
<span class="text-quaternary" style="font-size: 0.6rem">sessions</span>
|
||||
<span class="text-quaternary" style="font-size: 0.6rem"
|
||||
>sessions</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-primary font-heading">{{ rows.reduce((s, r) => s + r.distance, 0).toLocaleString() }}m</span>
|
||||
<span class="text-quaternary" style="font-size: 0.6rem">total dist</span>
|
||||
<span class="text-primary font-heading"
|
||||
>{{
|
||||
rows.reduce((s, r) => s + r.distance, 0).toLocaleString()
|
||||
}}m</span
|
||||
>
|
||||
<span class="text-quaternary" style="font-size: 0.6rem"
|
||||
>total dist</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-primary font-heading">{{ formatTime(rows.reduce((s, r) => s + r.timePer500m, 0) / (rows.length || 1)) }}</span>
|
||||
<span class="text-quaternary" style="font-size: 0.6rem">avg pace</span>
|
||||
<span class="text-primary font-heading">{{
|
||||
formatTime(
|
||||
rows.reduce((s, r) => s + r.timePer500m, 0) / (rows.length || 1),
|
||||
)
|
||||
}}</span>
|
||||
<span class="text-quaternary" style="font-size: 0.6rem"
|
||||
>avg pace</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -264,7 +310,9 @@ function formatValue(key, val) {
|
||||
<style scoped>
|
||||
.metric-btn {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s, color 0.15s;
|
||||
transition:
|
||||
background-color 0.15s,
|
||||
color 0.15s;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user