mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-09 07:47:58 -05:00
Improve formatting, docstrings & fix activityActivityStreams prop
[backend] fixed formatting [backend] changed existing docstrings to be more complete [backend] changed imports to be consistent with rest of the code [frontend] fixed formatting [frontend] set new prop on ActivitySummaryComponent to be not required because it is not always necessary (HomeView) [frontend] fixed issue onMounted on ActivitySummaryComponent when new prop is null
This commit is contained in:
@@ -3,6 +3,7 @@ from sqlalchemy.orm import Session
|
||||
import numpy as np
|
||||
import datetime
|
||||
|
||||
import activities.activity_streams.constants as activity_streams_constants
|
||||
import activities.activity_streams.schema as activity_streams_schema
|
||||
import activities.activity_streams.models as activity_streams_models
|
||||
|
||||
@@ -16,14 +17,12 @@ import users.user.crud as users_crud
|
||||
|
||||
import core.logger as core_logger
|
||||
|
||||
import activities.activity_streams.constants as stream_constants
|
||||
|
||||
|
||||
def get_activity_streams(activity_id: int, token_user_id: int, db: Session):
|
||||
def get_activity_streams(
|
||||
activity_id: int, token_user_id: int, db: Session
|
||||
) -> list[activity_streams_schema.ActivityStreams] | None:
|
||||
try:
|
||||
activity = activity_crud.get_activity_by_id(
|
||||
activity_id, db
|
||||
)
|
||||
activity = activity_crud.get_activity_by_id(activity_id, db)
|
||||
|
||||
if not activity:
|
||||
# If the activity does not exist, return None
|
||||
@@ -48,20 +47,52 @@ def get_activity_streams(activity_id: int, token_user_id: int, db: Session):
|
||||
|
||||
if not user_is_owner:
|
||||
activity_streams = [
|
||||
stream for stream in activity_streams
|
||||
stream
|
||||
for stream in activity_streams
|
||||
if not (
|
||||
(activity.hide_hr and stream.stream_type == stream_constants.STREAM_TYPE_HR) or
|
||||
(activity.hide_power and stream.stream_type == stream_constants.STREAM_TYPE_POWER) or
|
||||
(activity.hide_cadence and stream.stream_type == stream_constants.STREAM_TYPE_CADENCE) or
|
||||
(activity.hide_elevation and stream.stream_type == stream_constants.STREAM_TYPE_ELEVATION) or
|
||||
(activity.hide_speed and stream.stream_type == stream_constants.STREAM_TYPE_SPEED) or
|
||||
(activity.hide_pace and stream.stream_type == stream_constants.STREAM_TYPE_PACE) or
|
||||
(activity.hide_map and stream.stream_type == stream_constants.STREAM_TYPE_MAP)
|
||||
(
|
||||
activity.hide_hr
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_HR
|
||||
)
|
||||
or (
|
||||
activity.hide_power
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_POWER
|
||||
)
|
||||
or (
|
||||
activity.hide_cadence
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_CADENCE
|
||||
)
|
||||
or (
|
||||
activity.hide_elevation
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_ELEVATION
|
||||
)
|
||||
or (
|
||||
activity.hide_speed
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_SPEED
|
||||
)
|
||||
or (
|
||||
activity.hide_pace
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_PACE
|
||||
)
|
||||
or (
|
||||
activity.hide_map
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_MAP
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
# Return the activity streams
|
||||
return [transform_activity_streams(stream, activity, db) for stream in activity_streams]
|
||||
return [
|
||||
transform_activity_streams(stream, activity, db)
|
||||
for stream in activity_streams
|
||||
]
|
||||
except Exception as err:
|
||||
# Log the exception
|
||||
core_logger.print_to_log(
|
||||
@@ -82,10 +113,8 @@ def get_public_activity_streams(activity_id: int, db: Session):
|
||||
# Return None if public sharable links are disabled
|
||||
if not server_settings or not server_settings.public_shareable_links:
|
||||
return None
|
||||
|
||||
activity = activity_crud.get_activity_by_id_if_is_public(
|
||||
activity_id, db
|
||||
)
|
||||
|
||||
activity = activity_crud.get_activity_by_id_if_is_public(activity_id, db)
|
||||
|
||||
if not activity:
|
||||
# If the activity does not exist, return None
|
||||
@@ -110,22 +139,52 @@ def get_public_activity_streams(activity_id: int, db: Session):
|
||||
# Check if there are activity streams, if not return None
|
||||
if not activity_streams:
|
||||
return None
|
||||
|
||||
|
||||
activity_streams = [
|
||||
stream for stream in activity_streams
|
||||
stream
|
||||
for stream in activity_streams
|
||||
if not (
|
||||
(activity.hide_hr and stream.stream_type == stream_constants.STREAM_TYPE_HR) or
|
||||
(activity.hide_power and stream.stream_type == stream_constants.STREAM_TYPE_POWER) or
|
||||
(activity.hide_cadence and stream.stream_type == stream_constants.STREAM_TYPE_CADENCE) or
|
||||
(activity.hide_elevation and stream.stream_type == stream_constants.STREAM_TYPE_ELEVATION) or
|
||||
(activity.hide_speed and stream.stream_type == stream_constants.STREAM_TYPE_SPEED) or
|
||||
(activity.hide_pace and stream.stream_type == stream_constants.STREAM_TYPE_PACE) or
|
||||
(activity.hide_map and stream.stream_type == stream_constants.STREAM_TYPE_MAP)
|
||||
(
|
||||
activity.hide_hr
|
||||
and stream.stream_type == activity_streams_constants.STREAM_TYPE_HR
|
||||
)
|
||||
or (
|
||||
activity.hide_power
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_POWER
|
||||
)
|
||||
or (
|
||||
activity.hide_cadence
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_CADENCE
|
||||
)
|
||||
or (
|
||||
activity.hide_elevation
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_ELEVATION
|
||||
)
|
||||
or (
|
||||
activity.hide_speed
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_SPEED
|
||||
)
|
||||
or (
|
||||
activity.hide_pace
|
||||
and stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_PACE
|
||||
)
|
||||
or (
|
||||
activity.hide_map
|
||||
and stream.stream_type == activity_streams_constants.STREAM_TYPE_MAP
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
# Return the activity streams
|
||||
return [transform_activity_streams(stream, activity, db) for stream in activity_streams]
|
||||
return [
|
||||
transform_activity_streams(stream, activity, db)
|
||||
for stream in activity_streams
|
||||
]
|
||||
except Exception as err:
|
||||
# Log the exception
|
||||
core_logger.print_to_log(
|
||||
@@ -138,16 +197,16 @@ def get_public_activity_streams(activity_id: int, db: Session):
|
||||
) from err
|
||||
|
||||
|
||||
def get_activity_stream_by_type(activity_id: int, stream_type: int, token_user_id: int, db: Session):
|
||||
def get_activity_stream_by_type(
|
||||
activity_id: int, stream_type: int, token_user_id: int, db: Session
|
||||
):
|
||||
try:
|
||||
activity = activity_crud.get_activity_by_id(
|
||||
activity_id, db
|
||||
)
|
||||
activity = activity_crud.get_activity_by_id(activity_id, db)
|
||||
|
||||
if not activity:
|
||||
# If the activity does not exist, return None
|
||||
return None
|
||||
|
||||
|
||||
# Get the activity stream from the database
|
||||
activity_stream = (
|
||||
db.query(activity_streams_models.ActivityStreams)
|
||||
@@ -161,25 +220,53 @@ def get_activity_stream_by_type(activity_id: int, stream_type: int, token_user_i
|
||||
# Check if there are activity stream if not return None
|
||||
if not activity_stream:
|
||||
return None
|
||||
|
||||
|
||||
user_is_owner = True
|
||||
if token_user_id != activity.user_id:
|
||||
user_is_owner = False
|
||||
|
||||
if not user_is_owner:
|
||||
if activity.hide_hr and activity_stream.stream_type == stream_constants.STREAM_TYPE_HR:
|
||||
if (
|
||||
activity.hide_hr
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_HR
|
||||
):
|
||||
return None
|
||||
if activity.hide_power and activity_stream.stream_type == stream_constants.STREAM_TYPE_POWER:
|
||||
if (
|
||||
activity.hide_power
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_POWER
|
||||
):
|
||||
return None
|
||||
if activity.hide_cadence and activity_stream.stream_type == stream_constants.STREAM_TYPE_CADENCE:
|
||||
if (
|
||||
activity.hide_cadence
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_CADENCE
|
||||
):
|
||||
return None
|
||||
if activity.hide_elevation and activity_stream.stream_type == stream_constants.STREAM_TYPE_ELEVATION:
|
||||
if (
|
||||
activity.hide_elevation
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_ELEVATION
|
||||
):
|
||||
return None
|
||||
if activity.hide_speed and activity_stream.stream_type == stream_constants.STREAM_TYPE_SPEED:
|
||||
if (
|
||||
activity.hide_speed
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_SPEED
|
||||
):
|
||||
return None
|
||||
if activity.hide_pace and activity_stream.stream_type == stream_constants.STREAM_TYPE_PACE:
|
||||
if (
|
||||
activity.hide_pace
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_PACE
|
||||
):
|
||||
return None
|
||||
if activity.hide_map and activity_stream.stream_type == stream_constants.STREAM_TYPE_MAP:
|
||||
if (
|
||||
activity.hide_map
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_MAP
|
||||
):
|
||||
return None
|
||||
|
||||
# Return the activity stream
|
||||
@@ -195,43 +282,72 @@ def get_activity_stream_by_type(activity_id: int, stream_type: int, token_user_i
|
||||
detail="Internal Server Error",
|
||||
) from err
|
||||
|
||||
|
||||
def transform_activity_streams(activity_stream, activity, db):
|
||||
"""
|
||||
Transform the activity stream based on the stream type.
|
||||
Transforms an activity stream based on its stream type.
|
||||
If the stream type of the given activity_stream is heart rate (HR), this function delegates
|
||||
the transformation to the `transform_activity_streams_hr` function. Otherwise, it returns
|
||||
the activity_stream unchanged.
|
||||
Args:
|
||||
activity_stream: The activity stream object to be transformed.
|
||||
activity: The activity object associated with the stream.
|
||||
db: The database session or connection object.
|
||||
Returns:
|
||||
The transformed activity stream if the stream type is HR, otherwise the original activity_stream.
|
||||
"""
|
||||
if activity_stream.stream_type == stream_constants.STREAM_TYPE_HR:
|
||||
if activity_stream.stream_type == activity_streams_constants.STREAM_TYPE_HR:
|
||||
return transform_activity_streams_hr(activity_stream, activity, db)
|
||||
else:
|
||||
return activity_stream
|
||||
|
||||
return activity_stream
|
||||
|
||||
|
||||
def transform_activity_streams_hr(activity_stream, activity, db):
|
||||
"""
|
||||
Transform the activity stream for heart rate.
|
||||
Calculate the percentage of time spent in each HR zone using numpy for performance.
|
||||
Transforms an activity stream by calculating the percentage of time spent in each heart rate zone based on user details.
|
||||
Args:
|
||||
activity_stream: The activity stream object containing waypoints with heart rate data.
|
||||
activity: The activity object associated with the stream, used to retrieve the user ID.
|
||||
db: The database session or connection used to fetch user details.
|
||||
Returns:
|
||||
The activity stream object with an added 'hr_zone_percentages' attribute, which contains the percentage of time spent in each heart rate zone and their respective HR boundaries. If waypoints or user details are missing, returns the original activity stream unchanged.
|
||||
Notes:
|
||||
- Heart rate zones are calculated using the formula: max_heart_rate = 220 - age.
|
||||
- The function expects waypoints to be a list of dicts with an "hr" key.
|
||||
- If no valid heart rate data is present, the activity stream is returned as is.
|
||||
"""
|
||||
# Check if the activity stream has waypoints
|
||||
waypoints = activity_stream.stream_waypoints
|
||||
if not waypoints or not isinstance(waypoints, list):
|
||||
# If there are no waypoints, return the activity stream as is
|
||||
return activity_stream
|
||||
|
||||
# Get the user details to calculate heart rate zones
|
||||
detail_user = users_crud.get_user_by_id(activity.user_id, db)
|
||||
if not detail_user or not detail_user.birthdate:
|
||||
# If user details are not available or birthdate is missing, return the activity stream as is
|
||||
return activity_stream
|
||||
|
||||
# Calculate the maximum heart rate based on the user's birthdate
|
||||
year = int(detail_user.birthdate.split("-")[0])
|
||||
current_year = datetime.datetime.now().year
|
||||
max_heart_rate = 220 - (current_year - year)
|
||||
|
||||
# Calculate heart rate zones based on the maximum heart rate
|
||||
zone_1 = max_heart_rate * 0.5
|
||||
zone_2 = max_heart_rate * 0.6
|
||||
zone_3 = max_heart_rate * 0.7
|
||||
zone_4 = max_heart_rate * 0.8
|
||||
|
||||
waypoints = activity_stream.stream_waypoints
|
||||
if not waypoints or not isinstance(waypoints, list):
|
||||
return activity_stream
|
||||
|
||||
# Extract heart rate values from waypoints
|
||||
hr_values = np.array([wp.get("hr") for wp in waypoints if wp.get("hr") is not None])
|
||||
|
||||
# If there are no valid heart rate values, return the activity stream as is
|
||||
total = len(hr_values)
|
||||
|
||||
if total == 0:
|
||||
return activity_stream
|
||||
|
||||
|
||||
# Calculate the percentage of time spent in each heart rate zone
|
||||
zone_counts = [
|
||||
np.sum(hr_values < zone_1),
|
||||
np.sum((hr_values >= zone_1) & (hr_values < zone_2)),
|
||||
@@ -259,6 +375,7 @@ def transform_activity_streams_hr(activity_stream, activity, db):
|
||||
|
||||
return activity_stream
|
||||
|
||||
|
||||
def get_public_activity_stream_by_type(activity_id: int, stream_type: int, db: Session):
|
||||
try:
|
||||
# Check if public sharable links are enabled in server settings
|
||||
@@ -267,10 +384,8 @@ def get_public_activity_stream_by_type(activity_id: int, stream_type: int, db: S
|
||||
# Return None if public sharable links are disabled
|
||||
if not server_settings or not server_settings.public_shareable_links:
|
||||
return None
|
||||
|
||||
activity = activity_crud.get_activity_by_id_if_is_public(
|
||||
activity_id, db
|
||||
)
|
||||
|
||||
activity = activity_crud.get_activity_by_id_if_is_public(activity_id, db)
|
||||
|
||||
if not activity:
|
||||
# If the activity does not exist, return None
|
||||
@@ -296,20 +411,47 @@ def get_public_activity_stream_by_type(activity_id: int, stream_type: int, db: S
|
||||
# Check if there is an activity stream; if not, return None
|
||||
if not activity_stream:
|
||||
return None
|
||||
|
||||
if activity.hide_hr and activity_stream.stream_type == stream_constants.STREAM_TYPE_HR:
|
||||
|
||||
if (
|
||||
activity.hide_hr
|
||||
and activity_stream.stream_type == activity_streams_constants.STREAM_TYPE_HR
|
||||
):
|
||||
return None
|
||||
if activity.hide_power and activity_stream.stream_type == stream_constants.STREAM_TYPE_POWER:
|
||||
if (
|
||||
activity.hide_power
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_POWER
|
||||
):
|
||||
return None
|
||||
if activity.hide_cadence and activity_stream.stream_type == stream_constants.STREAM_TYPE_CADENCE:
|
||||
if (
|
||||
activity.hide_cadence
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_CADENCE
|
||||
):
|
||||
return None
|
||||
if activity.hide_elevation and activity_stream.stream_type == stream_constants.STREAM_TYPE_ELEVATION:
|
||||
if (
|
||||
activity.hide_elevation
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_ELEVATION
|
||||
):
|
||||
return None
|
||||
if activity.hide_speed and activity_stream.stream_type == stream_constants.STREAM_TYPE_SPEED:
|
||||
if (
|
||||
activity.hide_speed
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_SPEED
|
||||
):
|
||||
return None
|
||||
if activity.hide_pace and activity_stream.stream_type == stream_constants.STREAM_TYPE_PACE:
|
||||
if (
|
||||
activity.hide_pace
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_PACE
|
||||
):
|
||||
return None
|
||||
if activity.hide_map and activity_stream.stream_type == stream_constants.STREAM_TYPE_MAP:
|
||||
if (
|
||||
activity.hide_map
|
||||
and activity_stream.stream_type
|
||||
== activity_streams_constants.STREAM_TYPE_MAP
|
||||
):
|
||||
return None
|
||||
|
||||
# Return the activity stream
|
||||
|
||||
@@ -3,6 +3,17 @@ from typing import List
|
||||
|
||||
|
||||
class ActivityStreams(BaseModel):
|
||||
"""
|
||||
Represents a stream of activity data associated with an activity.
|
||||
|
||||
Attributes:
|
||||
id (int | None): Unique identifier for the activity stream (optional).
|
||||
activity_id (int): Identifier of the related activity.
|
||||
stream_type (int): Type of the stream (e.g., GPS, heart rate, etc.).
|
||||
stream_waypoints (List[dict]): List of waypoints or data points in the stream.
|
||||
strava_activity_stream_id (int | None): Identifier for the corresponding Strava activity stream (optional).
|
||||
hr_zone_percentages (dict | None): Heart rate zone percentages for the activity (optional).
|
||||
"""
|
||||
id: int | None = None
|
||||
activity_id: int
|
||||
stream_type: int
|
||||
@@ -11,4 +22,10 @@ class ActivityStreams(BaseModel):
|
||||
hr_zone_percentages: dict | None = None
|
||||
|
||||
class Config:
|
||||
"""
|
||||
Pydantic configuration class enabling ORM mode for model serialization.
|
||||
|
||||
Attributes:
|
||||
orm_mode (bool): When set to True, allows Pydantic models to read data from ORM objects.
|
||||
"""
|
||||
orm_mode = True
|
||||
@@ -646,8 +646,12 @@ def fetch_and_process_activity_laps(
|
||||
"total_elapsed_time": lap.elapsed_time,
|
||||
"total_timer_time": lap.moving_time,
|
||||
"total_distance": lap.distance,
|
||||
"avg_heart_rate": round(lap.average_heartrate) if lap.average_heartrate else None,
|
||||
"max_heart_rate": round(lap.max_heartrate) if lap.max_heartrate else None,
|
||||
"avg_heart_rate": (
|
||||
round(lap.average_heartrate) if lap.average_heartrate else None
|
||||
),
|
||||
"max_heart_rate": (
|
||||
round(lap.max_heartrate) if lap.max_heartrate else None
|
||||
),
|
||||
"avg_cadence": round(cad_avg) if cad_stream else None,
|
||||
"max_cadence": round(cad_max) if cad_stream else None,
|
||||
"avg_power": round(power_avg) if power_stream else None,
|
||||
|
||||
@@ -1,59 +1,81 @@
|
||||
<template>
|
||||
<ul class="nav nav-pills mb-3 mt-3 justify-content-center" id="pills-tab" role="tablist">
|
||||
<li class="nav-item" role="presentation" v-if="graphItems && graphItems.length > 0">
|
||||
<button class="nav-link link-body-emphasis" :class="{ active: graphItems || graphItems.length > 0 }" id="pills-graphs-tab" data-bs-toggle="pill" data-bs-target="#pills-graphs" type="button" role="tab" aria-controls="pills-graphs" :aria-selected="graphItems && graphItems.length > 0 ? true : false">
|
||||
{{ $t("activityMandAbovePillsComponent.labelPillGraphs") }}
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation" v-if="activityActivityLaps && activityActivityLaps.length > 0">
|
||||
<button class="nav-link link-body-emphasis" :class="{ active: !graphItems || graphItems.length === 0 }" id="pills-laps-tab" data-bs-toggle="pill" data-bs-target="#pills-laps" type="button" role="tab" aria-controls="pills-laps" :aria-selected="!graphItems || graphItems.length === 0 ? 'true' : 'false'">
|
||||
{{ $t("activityMandAbovePillsComponent.labelPillLaps") }}
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation" v-if="activityActivityWorkoutSteps && activityActivityWorkoutSteps.length > 0">
|
||||
<button class="nav-link link-body-emphasis" id="pills-workout-steps-tab" data-bs-toggle="pill" data-bs-target="#pills-workout-steps" type="button" role="tab" aria-controls="pills-workout-steps" aria-selected="false">
|
||||
{{ $t("activityMandAbovePillsComponent.labelPillWorkoutSets") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav nav-pills mb-3 mt-3 justify-content-center" id="pills-tab" role="tablist">
|
||||
<li class="nav-item" role="presentation" v-if="graphItems && graphItems.length > 0">
|
||||
<button class="nav-link link-body-emphasis" :class="{ active: graphItems || graphItems.length > 0 }"
|
||||
id="pills-graphs-tab" data-bs-toggle="pill" data-bs-target="#pills-graphs" type="button" role="tab"
|
||||
aria-controls="pills-graphs" :aria-selected="graphItems && graphItems.length > 0 ? true : false">
|
||||
{{ $t("activityMandAbovePillsComponent.labelPillGraphs") }}
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation" v-if="activityActivityLaps && activityActivityLaps.length > 0">
|
||||
<button class="nav-link link-body-emphasis" :class="{ active: !graphItems || graphItems.length === 0 }"
|
||||
id="pills-laps-tab" data-bs-toggle="pill" data-bs-target="#pills-laps" type="button" role="tab"
|
||||
aria-controls="pills-laps" :aria-selected="!graphItems || graphItems.length === 0 ? 'true' : 'false'">
|
||||
{{ $t("activityMandAbovePillsComponent.labelPillLaps") }}
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation"
|
||||
v-if="activityActivityWorkoutSteps && activityActivityWorkoutSteps.length > 0">
|
||||
<button class="nav-link link-body-emphasis" id="pills-workout-steps-tab" data-bs-toggle="pill"
|
||||
data-bs-target="#pills-workout-steps" type="button" role="tab" aria-controls="pills-workout-steps"
|
||||
aria-selected="false">
|
||||
{{ $t("activityMandAbovePillsComponent.labelPillWorkoutSets") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="pills-tabContent">
|
||||
<div class="tab-pane fade show" :class="{ active: graphItems || graphItems.length > 0 }" id="pills-graphs" role="tabpanel" aria-labelledby="pills-graphs-tab" tabindex="0" v-if="graphItems && graphItems.length > 0">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<p>{{ $t("activityMandAbovePillsComponent.labelGraph") }}</p>
|
||||
<ul class="nav nav-pills flex-column mb-auto" id="sidebarLineGraph">
|
||||
<li class="nav-item" v-for="item in graphItems" :key="item.type">
|
||||
<a href="javascript:void(0);" class="nav-link text-secondary"
|
||||
:class="{ 'active text-white': graphSelection === item.type }"
|
||||
@click="selectGraph(item.type)">
|
||||
{{ item.label }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="mt-2">{{ $t("activityMandAbovePillsComponent.labelDownsampling") }}</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div if="activity">
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection" :activityStreams="activityActivityStreams" v-if="graphSelection === 'hr' && hrPresent"/>
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection" :activityStreams="activityActivityStreams" v-if="graphSelection === 'power' && powerPresent"/>
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection" :activityStreams="activityActivityStreams" v-if="graphSelection === 'cad' && cadPresent"/>
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection" :activityStreams="activityActivityStreams" v-if="graphSelection === 'ele' && elePresent"/>
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection" :activityStreams="activityActivityStreams" v-if="graphSelection === 'vel' && velPresent"/>
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection" :activityStreams="activityActivityStreams" v-if="graphSelection === 'pace' && pacePresent"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" :class="{ 'show active': !graphItems || graphItems.length === 0 }" id="pills-laps" role="tabpanel" aria-labelledby="pills-laps-tab" tabindex="1" v-if="activityActivityLaps && activityActivityLaps.length > 0">
|
||||
<ActivityLapsComponent :activity="activity" :activityActivityLaps="activityActivityLaps" :units="units" />
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-workout-steps" role="tabpanel" aria-labelledby="pills-workout-steps-tab" tabindex="2" v-if="activityActivityWorkoutSteps && activityActivityWorkoutSteps.length > 0">
|
||||
<ActivityWorkoutStepsComponent :activity="activity" :activityActivityWorkoutSteps="activityActivityWorkoutSteps" :units="units" :activityActivityExerciseTitles="activityActivityExerciseTitles" :activityActivitySets="activityActivitySets" />
|
||||
<div class="tab-content" id="pills-tabContent">
|
||||
<div class="tab-pane fade show" :class="{ active: graphItems || graphItems.length > 0 }" id="pills-graphs"
|
||||
role="tabpanel" aria-labelledby="pills-graphs-tab" tabindex="0" v-if="graphItems && graphItems.length > 0">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<p>{{ $t("activityMandAbovePillsComponent.labelGraph") }}</p>
|
||||
<ul class="nav nav-pills flex-column mb-auto" id="sidebarLineGraph">
|
||||
<li class="nav-item" v-for="item in graphItems" :key="item.type">
|
||||
<a href="javascript:void(0);" class="nav-link text-secondary"
|
||||
:class="{ 'active text-white': graphSelection === item.type }"
|
||||
@click="selectGraph(item.type)">
|
||||
{{ item.label }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="mt-2">{{ $t("activityMandAbovePillsComponent.labelDownsampling") }}</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div if="activity">
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection"
|
||||
:activityStreams="activityActivityStreams" v-if="graphSelection === 'hr' && hrPresent" />
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection"
|
||||
:activityStreams="activityActivityStreams"
|
||||
v-if="graphSelection === 'power' && powerPresent" />
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection"
|
||||
:activityStreams="activityActivityStreams" v-if="graphSelection === 'cad' && cadPresent" />
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection"
|
||||
:activityStreams="activityActivityStreams" v-if="graphSelection === 'ele' && elePresent" />
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection"
|
||||
:activityStreams="activityActivityStreams" v-if="graphSelection === 'vel' && velPresent" />
|
||||
<ActivityStreamsLineChartComponent :activity="activity" :graphSelection="graphSelection"
|
||||
:activityStreams="activityActivityStreams"
|
||||
v-if="graphSelection === 'pace' && pacePresent" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" :class="{ 'show active': !graphItems || graphItems.length === 0 }" id="pills-laps"
|
||||
role="tabpanel" aria-labelledby="pills-laps-tab" tabindex="1"
|
||||
v-if="activityActivityLaps && activityActivityLaps.length > 0">
|
||||
<ActivityLapsComponent :activity="activity" :activityActivityLaps="activityActivityLaps" :units="units" />
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-workout-steps" role="tabpanel" aria-labelledby="pills-workout-steps-tab"
|
||||
tabindex="2" v-if="activityActivityWorkoutSteps && activityActivityWorkoutSteps.length > 0">
|
||||
<ActivityWorkoutStepsComponent :activity="activity"
|
||||
:activityActivityWorkoutSteps="activityActivityWorkoutSteps" :units="units"
|
||||
:activityActivityExerciseTitles="activityActivityExerciseTitles"
|
||||
:activityActivitySets="activityActivitySets" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
@@ -6,15 +6,19 @@
|
||||
<div class="d-flex justify-content-between">
|
||||
<!-- user name and photo zone -->
|
||||
<div class="d-flex align-items-center">
|
||||
<UserAvatarComponent :user="userActivity" :width=55 :height=55 />
|
||||
<UserAvatarComponent :user="userActivity" :width=55 :height=55 />
|
||||
<div class="ms-3 me-3">
|
||||
<div class="fw-bold">
|
||||
<router-link :to="{ name: 'activity', params: { id: activity.id }}" class="link-body-emphasis link-underline-opacity-0 link-underline-opacity-100-hover" v-if="source === 'home'">
|
||||
{{ activity.name}}
|
||||
<router-link :to="{ name: 'activity', params: { id: activity.id } }"
|
||||
class="link-body-emphasis link-underline-opacity-0 link-underline-opacity-100-hover"
|
||||
v-if="source === 'home'">
|
||||
{{ activity.name }}
|
||||
</router-link>
|
||||
<span v-if="userActivity">
|
||||
<router-link :to="{ name: 'user', params: { id: userActivity.id }}" class="link-body-emphasis link-underline-opacity-0 link-underline-opacity-100-hover" v-if="source === 'activity'">
|
||||
{{ userActivity.name}}
|
||||
<router-link :to="{ name: 'user', params: { id: userActivity.id } }"
|
||||
class="link-body-emphasis link-underline-opacity-0 link-underline-opacity-100-hover"
|
||||
v-if="source === 'activity'">
|
||||
{{ userActivity.name }}
|
||||
</router-link>
|
||||
</span>
|
||||
<span v-else>
|
||||
@@ -24,20 +28,24 @@
|
||||
<h6>
|
||||
<!-- Display the visibility of the activity -->
|
||||
<span v-if="activity.visibility == 0">
|
||||
<font-awesome-icon :icon="['fas', 'globe']"/> {{ $t("activitySummaryComponent.visibilityPublic") }}
|
||||
<font-awesome-icon :icon="['fas', 'globe']" /> {{
|
||||
$t("activitySummaryComponent.visibilityPublic") }}
|
||||
</span>
|
||||
<span v-if="activity.visibility == 1">
|
||||
<font-awesome-icon :icon="['fas', 'users']" v-if="activity.visibility == 1" /> {{ $t("activitySummaryComponent.visibilityFollowers") }}
|
||||
<font-awesome-icon :icon="['fas', 'users']" v-if="activity.visibility == 1" /> {{
|
||||
$t("activitySummaryComponent.visibilityFollowers") }}
|
||||
</span>
|
||||
<span v-if="activity.visibility == 2">
|
||||
<font-awesome-icon :icon="['fas', 'lock']" v-if="activity.visibility == 2" /> {{ $t("activitySummaryComponent.visibilityPrivate") }}
|
||||
<font-awesome-icon :icon="['fas', 'lock']" v-if="activity.visibility == 2" /> {{
|
||||
$t("activitySummaryComponent.visibilityPrivate") }}
|
||||
</span>
|
||||
<span> - </span>
|
||||
|
||||
<!-- Display the activity type -->
|
||||
<span>
|
||||
<font-awesome-icon class="me-1" :icon="getIcon(activity.activity_type)" />
|
||||
<span v-if="activity.activity_type === 3 || activity.activity_type === 7">{{ $t("activitySummaryComponent.labelVirtual") }}</span>
|
||||
<span v-if="activity.activity_type === 3 || activity.activity_type === 7">{{
|
||||
$t("activitySummaryComponent.labelVirtual") }}</span>
|
||||
</span>
|
||||
|
||||
<!-- Display the date and time -->
|
||||
@@ -46,32 +54,42 @@
|
||||
</span>
|
||||
<!-- Conditionally display city and country -->
|
||||
<span v-if="activity.town || activity.city || activity.country">
|
||||
-
|
||||
-
|
||||
<span>{{ formatLocation(activity) }}</span>
|
||||
</span>
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown d-flex" v-if="activity.user_id == authStore.user.id">
|
||||
<a class="btn btn-link btn-lg link-body-emphasis" :href="`https://www.strava.com/activities/${activity.strava_activity_id}`" role="button" v-if="activity.strava_activity_id">
|
||||
<a class="btn btn-link btn-lg link-body-emphasis"
|
||||
:href="`https://www.strava.com/activities/${activity.strava_activity_id}`" role="button"
|
||||
v-if="activity.strava_activity_id">
|
||||
<font-awesome-icon :icon="['fab', 'fa-strava']" />
|
||||
</a>
|
||||
<a class="btn btn-link btn-lg link-body-emphasis" :href="`https://connect.garmin.com/modern/activity/${activity.garminconnect_activity_id}`" role="button" v-if="activity.garminconnect_activity_id">
|
||||
<img src="/src/assets/garminconnect/Garmin_Connect_app_1024x1024-02.png" alt="Garmin Connect logo" height="22" />
|
||||
<a class="btn btn-link btn-lg link-body-emphasis"
|
||||
:href="`https://connect.garmin.com/modern/activity/${activity.garminconnect_activity_id}`"
|
||||
role="button" v-if="activity.garminconnect_activity_id">
|
||||
<img src="/src/assets/garminconnect/Garmin_Connect_app_1024x1024-02.png" alt="Garmin Connect logo"
|
||||
height="22" />
|
||||
</a>
|
||||
<div>
|
||||
<button class="btn btn-link btn-lg link-body-emphasis" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<button class="btn btn-link btn-lg link-body-emphasis" type="button" data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<font-awesome-icon :icon="['fas', 'fa-ellipsis-vertical']" />
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li v-if="source === 'activity'">
|
||||
<a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#editActivityModal">
|
||||
<a class="dropdown-item" href="#" data-bs-toggle="modal"
|
||||
data-bs-target="#editActivityModal">
|
||||
{{ $t("activitySummaryComponent.buttonEditActivity") }}
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="source === 'activity'"><hr class="dropdown-divider"></li>
|
||||
<li v-if="source === 'activity'">
|
||||
<hr class="dropdown-divider">
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#deleteActivityModal">
|
||||
<a class="dropdown-item" href="#" data-bs-toggle="modal"
|
||||
data-bs-target="#deleteActivityModal">
|
||||
{{ $t("activitySummaryComponent.buttonDeleteActivity") }}
|
||||
</a>
|
||||
</li>
|
||||
@@ -81,10 +99,13 @@
|
||||
</div>
|
||||
|
||||
<!-- Modal edit activity -->
|
||||
<EditActivityModalComponent :activity="activity" @activityEditedFields="updateActivityFieldsOnEdit"/>
|
||||
<EditActivityModalComponent :activity="activity" @activityEditedFields="updateActivityFieldsOnEdit" />
|
||||
|
||||
<!-- Modal delete activity -->
|
||||
<ModalComponent modalId="deleteActivityModal" :title="t('activitySummaryComponent.buttonDeleteActivity')" :body="`${t('activitySummaryComponent.modalDeleteBody1')}<b>${activity.name}</b>?<br>${t('activitySummaryComponent.modalDeleteBody2')}`" :actionButtonType="`danger`" :actionButtonText="t('activitySummaryComponent.buttonDeleteActivity')" @submitAction="submitDeleteActivity"/>
|
||||
<ModalComponent modalId="deleteActivityModal" :title="t('activitySummaryComponent.buttonDeleteActivity')"
|
||||
:body="`${t('activitySummaryComponent.modalDeleteBody1')}<b>${activity.name}</b>?<br>${t('activitySummaryComponent.modalDeleteBody2')}`"
|
||||
:actionButtonType="`danger`" :actionButtonText="t('activitySummaryComponent.buttonDeleteActivity')"
|
||||
@submitAction="submitDeleteActivity" />
|
||||
|
||||
<!-- Activity title -->
|
||||
<h1 class="mt-3" v-if="source === 'activity'">
|
||||
@@ -97,7 +118,8 @@
|
||||
<!-- Activity summary -->
|
||||
<div class="row mt-3 align-items-center text-start">
|
||||
<!-- distance -->
|
||||
<div class="col border-start border-opacity-50" v-if="activity.activity_type != 10 && activity.activity_type != 14 && activity.activity_type != 18 && activity.activity_type != 19 && activity.activity_type != 20 && activity.activity_type != 21 && activity.activity_type != 22 && activity.activity_type != 23 && activity.activity_type != 24 && activity.activity_type != 25 && activity.activity_type != 26">
|
||||
<div class="col border-start border-opacity-50"
|
||||
v-if="activity.activity_type != 10 && activity.activity_type != 14 && activity.activity_type != 18 && activity.activity_type != 19 && activity.activity_type != 20 && activity.activity_type != 21 && activity.activity_type != 22 && activity.activity_type != 23 && activity.activity_type != 24 && activity.activity_type != 25 && activity.activity_type != 26">
|
||||
<span class="fw-lighter">
|
||||
{{ $t("activitySummaryComponent.activityDistance") }}
|
||||
</span>
|
||||
@@ -119,13 +141,16 @@
|
||||
</span>
|
||||
<br>
|
||||
<span>
|
||||
{{$t('activitySummaryComponent.activityMovingTime')}}: {{ formatSecondsToMinutes(activity.total_timer_time) }} <br>
|
||||
{{$t('activitySummaryComponent.activityTotalTime')}}: {{ formatSecondsToMinutes(activity.total_elapsed_time) }}
|
||||
{{ $t('activitySummaryComponent.activityMovingTime') }}: {{
|
||||
formatSecondsToMinutes(activity.total_timer_time) }} <br>
|
||||
{{ $t('activitySummaryComponent.activityTotalTime') }}: {{
|
||||
formatSecondsToMinutes(activity.total_elapsed_time) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="col border-start border-opacity-50">
|
||||
<!-- elevation -->
|
||||
<div v-if="activity.activity_type != 1 && activity.activity_type != 2 && activity.activity_type != 3 && activity.activity_type != 8 && activity.activity_type != 9 && activity.activity_type != 10 && activity.activity_type != 13 && activity.activity_type != 14 && activity.activity_type != 18 && activity.activity_type != 19 && activity.activity_type != 20 && activity.activity_type != 21 && activity.activity_type != 22 && activity.activity_type != 23 && activity.activity_type != 24 && activity.activity_type != 25 && activity.activity_type != 26">
|
||||
<div
|
||||
v-if="activity.activity_type != 1 && activity.activity_type != 2 && activity.activity_type != 3 && activity.activity_type != 8 && activity.activity_type != 9 && activity.activity_type != 10 && activity.activity_type != 13 && activity.activity_type != 14 && activity.activity_type != 18 && activity.activity_type != 19 && activity.activity_type != 20 && activity.activity_type != 21 && activity.activity_type != 22 && activity.activity_type != 23 && activity.activity_type != 24 && activity.activity_type != 25 && activity.activity_type != 26">
|
||||
<span class="fw-lighter">
|
||||
{{ $t("activitySummaryComponent.activityElevationGain") }}
|
||||
</span>
|
||||
@@ -133,7 +158,8 @@
|
||||
<span>{{ formatElevation(activity.elevation_gain, authStore.user.units) }}</span>
|
||||
</div>
|
||||
<!-- pace -->
|
||||
<div v-else-if="activity.activity_type != 10 && activity.activity_type != 14 && activity.activity_type != 18 && activity.activity_type != 19 && activity.activity_type != 20 && activity.activity_type != 21 && activity.activity_type != 22 && activity.activity_type != 23 && activity.activity_type != 24 && activity.activity_type != 25 && activity.activity_type != 26">
|
||||
<div
|
||||
v-else-if="activity.activity_type != 10 && activity.activity_type != 14 && activity.activity_type != 18 && activity.activity_type != 19 && activity.activity_type != 20 && activity.activity_type != 21 && activity.activity_type != 22 && activity.activity_type != 23 && activity.activity_type != 24 && activity.activity_type != 25 && activity.activity_type != 26">
|
||||
<span class="fw-lighter">
|
||||
{{ $t("activitySummaryComponent.activityPace") }}
|
||||
</span>
|
||||
@@ -141,10 +167,12 @@
|
||||
{{ formatPace(activity, authStore.user.units) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row d-flex mt-3" v-if="source === 'activity' && activity.activity_type != 10 && activity.activity_type != 14 && activity.activity_type != 18 && activity.activity_type != 19 && activity.activity_type != 20 && activity.activity_type != 21 && activity.activity_type != 22 && activity.activity_type != 23 && activity.activity_type != 24 && activity.activity_type != 25 && activity.activity_type != 26">
|
||||
</div>
|
||||
<div class="row d-flex mt-3"
|
||||
v-if="source === 'activity' && activity.activity_type != 10 && activity.activity_type != 14 && activity.activity_type != 18 && activity.activity_type != 19 && activity.activity_type != 20 && activity.activity_type != 21 && activity.activity_type != 22 && activity.activity_type != 23 && activity.activity_type != 24 && activity.activity_type != 25 && activity.activity_type != 26">
|
||||
<!-- avg_power running and cycling activities-->
|
||||
<div class="col border-start border-opacity-50" v-if="activity.activity_type == 1 || activity.activity_type == 2 || activity.activity_type == 3 || activity.activity_type == 4 || activity.activity_type == 5 || activity.activity_type == 6 || activity.activity_type == 7 || activity.activity_type == 27">
|
||||
<div class="col border-start border-opacity-50"
|
||||
v-if="activity.activity_type == 1 || activity.activity_type == 2 || activity.activity_type == 3 || activity.activity_type == 4 || activity.activity_type == 5 || activity.activity_type == 6 || activity.activity_type == 7 || activity.activity_type == 27">
|
||||
<span class="fw-lighter">
|
||||
{{ $t("activitySummaryComponent.activityAvgPower") }}
|
||||
</span>
|
||||
@@ -152,7 +180,8 @@
|
||||
<span>{{ formatPower(activity.average_power) }}</span>
|
||||
</div>
|
||||
<!-- avg_hr not running and cycling activities-->
|
||||
<div class="col" v-if="activity.activity_type != 1 && activity.activity_type != 2 && activity.activity_type != 3 && activity.activity_type != 4 && activity.activity_type != 5 && activity.activity_type != 6 && activity.activity_type != 7 && activity.activity_type != 27">
|
||||
<div class="col"
|
||||
v-if="activity.activity_type != 1 && activity.activity_type != 2 && activity.activity_type != 3 && activity.activity_type != 4 && activity.activity_type != 5 && activity.activity_type != 6 && activity.activity_type != 7 && activity.activity_type != 27">
|
||||
<span class="fw-lighter">
|
||||
{{ $t("activitySummaryComponent.activityAvgHR") }}
|
||||
</span>
|
||||
@@ -160,7 +189,8 @@
|
||||
<span>{{ formatHr(activity.average_hr) }}</span>
|
||||
</div>
|
||||
<!-- max_hr not running and cycling activities-->
|
||||
<div class="col border-start border-opacity-50" v-if="activity.activity_type != 1 && activity.activity_type != 2 && activity.activity_type != 3 && activity.activity_type != 4 && activity.activity_type != 5 && activity.activity_type != 6 && activity.activity_type != 7 && activity.activity_type != 27">
|
||||
<div class="col border-start border-opacity-50"
|
||||
v-if="activity.activity_type != 1 && activity.activity_type != 2 && activity.activity_type != 3 && activity.activity_type != 4 && activity.activity_type != 5 && activity.activity_type != 6 && activity.activity_type != 7 && activity.activity_type != 27">
|
||||
<span class="fw-lighter">
|
||||
{{ $t("activitySummaryComponent.activityMaxHR") }}
|
||||
</span>
|
||||
@@ -168,13 +198,15 @@
|
||||
<span>{{ formatHr(activity.max_hr) }}</span>
|
||||
</div>
|
||||
<!-- ele gain running activities -->
|
||||
<div class="col border-start border-opacity-50" v-if="activity.activity_type == 1 || activity.activity_type == 2 || activity.activity_type == 3">
|
||||
<div class="col border-start border-opacity-50"
|
||||
v-if="activity.activity_type == 1 || activity.activity_type == 2 || activity.activity_type == 3">
|
||||
<span class="fw-lighter">{{ $t("activitySummaryComponent.activityEleGain") }}</span>
|
||||
<br>
|
||||
<span>{{ formatElevation(activity.elevation_gain, authStore.user.units) }}</span>
|
||||
</div>
|
||||
<!-- avg_speed cycling activities -->
|
||||
<div class="col border-start border-opacity-50" v-if="activity.activity_type == 4 || activity.activity_type == 5 || activity.activity_type == 6 || activity.activity_type == 7 || activity.activity_type == 27">
|
||||
<div class="col border-start border-opacity-50"
|
||||
v-if="activity.activity_type == 4 || activity.activity_type == 5 || activity.activity_type == 6 || activity.activity_type == 7 || activity.activity_type == 27">
|
||||
<span class="fw-lighter">
|
||||
{{ $t("activitySummaryComponent.activityAvgSpeed") }}
|
||||
</span>
|
||||
@@ -190,24 +222,23 @@
|
||||
<span>{{ formatCalories(activity.calories) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row d-flex mt-3" v-if="source === 'activity' && activity.activity_type != 10 && activity.activity_type != 14 && activity.activity_type != 18 && activity.activity_type != 19 && activity.activity_type != 20 && activity.activity_type != 21 && activity.activity_type != 22 && activity.activity_type != 23 && activity.activity_type != 24 && activity.activity_type != 25 && activity.activity_type != 26">
|
||||
<div class="row d-flex mt-3"
|
||||
v-if="source === 'activity' && activity.activity_type != 10 && activity.activity_type != 14 && activity.activity_type != 18 && activity.activity_type != 19 && activity.activity_type != 20 && activity.activity_type != 21 && activity.activity_type != 22 && activity.activity_type != 23 && activity.activity_type != 24 && activity.activity_type != 25 && activity.activity_type != 26">
|
||||
<div class="col border-start border-opacity-50">
|
||||
<!-- hr -->
|
||||
<div>
|
||||
<span class="fw-lighter">
|
||||
{{ $t("activitySummaryComponent.activityHR") }}
|
||||
{{ $t("activitySummaryComponent.activityHR") }}
|
||||
</span>
|
||||
<br>
|
||||
<span>{{ $t("activitySummaryComponent.activityAvgHR") }}: {{ formatHr(activity.average_hr) }}</span> <br>
|
||||
<span>{{ $t("activitySummaryComponent.activityMaxHR") }}: {{ formatHr(activity.max_hr) }}</span> <br><br>
|
||||
<BarChartComponent
|
||||
v-if="Object.values(hrZones).length > 0"
|
||||
:labels="getHrBarChartData().labels"
|
||||
:values="getHrBarChartData().values"
|
||||
:barColors="getHrBarChartData().barColors"
|
||||
:datalabelsFormatter="(value) => `${Math.round(value)}%`"
|
||||
:title="$t('activitySummaryComponent.activityHRZone')"
|
||||
/>
|
||||
<span>{{ $t("activitySummaryComponent.activityAvgHR") }}: {{ formatHr(activity.average_hr) }}</span>
|
||||
<br>
|
||||
<span>{{ $t("activitySummaryComponent.activityMaxHR") }}: {{ formatHr(activity.max_hr) }}</span>
|
||||
<br><br>
|
||||
<BarChartComponent v-if="Object.values(hrZones).length > 0" :labels="getHrBarChartData().labels"
|
||||
:values="getHrBarChartData().values" :barColors="getHrBarChartData().barColors"
|
||||
:datalabelsFormatter="(value) => `${Math.round(value)}%`"
|
||||
:title="$t('activitySummaryComponent.activityHRZone')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -234,36 +265,36 @@ import { users } from "@/services/usersService";
|
||||
import { activities } from "@/services/activitiesService";
|
||||
// Importing the utils
|
||||
import {
|
||||
formatDistance,
|
||||
formatElevation,
|
||||
formatPace,
|
||||
formatHr,
|
||||
formatCalories,
|
||||
getIcon,
|
||||
formatLocation,
|
||||
formatDistance,
|
||||
formatElevation,
|
||||
formatPace,
|
||||
formatHr,
|
||||
formatCalories,
|
||||
getIcon,
|
||||
formatLocation,
|
||||
formatAverageSpeed,
|
||||
formatPower,
|
||||
} from "@/utils/activityUtils";
|
||||
import {
|
||||
formatDateMed,
|
||||
formatTime,
|
||||
formatDateMed,
|
||||
formatTime,
|
||||
formatSecondsToMinutes,
|
||||
} from "@/utils/dateTimeUtils";
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
activity: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
activity: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
activityActivityStreams: {
|
||||
type: [Object, null],
|
||||
required: true,
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: [Object, null],
|
||||
required: false,
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
units: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
@@ -292,8 +323,10 @@ const hrZones = ref({
|
||||
|
||||
// Lifecycle
|
||||
onMounted(async () => {
|
||||
try {
|
||||
hrZones.value = props.activityActivityStreams.find(stream => stream.hr_zone_percentages).hr_zone_percentages || {};
|
||||
try {
|
||||
if (props.activityActivityStreams && props.activityActivityStreams.length > 0) {
|
||||
hrZones.value = props.activityActivityStreams.find(stream => stream.hr_zone_percentages).hr_zone_percentages || {};
|
||||
}
|
||||
|
||||
if (authStore.isAuthenticated) {
|
||||
userActivity.value = await users.getUserById(props.activity.user_id);
|
||||
@@ -302,17 +335,17 @@ onMounted(async () => {
|
||||
userActivity.value = await users.getPublicUserById(props.activity.user_id);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
push.error(`${t("activitySummaryComponent.errorFetchingUserById")} - ${error}`);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
} catch (error) {
|
||||
push.error(`${t("activitySummaryComponent.errorFetchingUserById")} - ${error}`);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Methods
|
||||
async function submitDeleteActivity() {
|
||||
try {
|
||||
userActivity.value = await activities.deleteActivity(props.activity.id);
|
||||
try {
|
||||
userActivity.value = await activities.deleteActivity(props.activity.id);
|
||||
if (props.source === 'activity') {
|
||||
return router.push({
|
||||
path: "/",
|
||||
@@ -320,35 +353,35 @@ async function submitDeleteActivity() {
|
||||
});
|
||||
}
|
||||
emit("activityDeleted", props.activity.id);
|
||||
} catch (error) {
|
||||
push.error(`${t("activitySummaryComponent.errorDeletingActivity")} - ${error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
push.error(`${t("activitySummaryComponent.errorDeletingActivity")} - ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
function updateActivityFieldsOnEdit(data) {
|
||||
// Emit the activityEditedFields event to the parent component
|
||||
emit("activityEditedFields", data);
|
||||
// Emit the activityEditedFields event to the parent component
|
||||
emit("activityEditedFields", data);
|
||||
}
|
||||
|
||||
function getZoneColor(index) {
|
||||
// Example colors for 5 HR zones
|
||||
const colors = [
|
||||
'#1e90ff', // Zone 1: blue
|
||||
'#28a745', // Zone 2: green
|
||||
'#ffc107', // Zone 3: yellow
|
||||
'#fd7e14', // Zone 4: orange
|
||||
'#dc3545', // Zone 5: red
|
||||
];
|
||||
return colors[index] || '#000';
|
||||
// Example colors for 5 HR zones
|
||||
const colors = [
|
||||
'#1e90ff', // Zone 1: blue
|
||||
'#28a745', // Zone 2: green
|
||||
'#ffc107', // Zone 3: yellow
|
||||
'#fd7e14', // Zone 4: orange
|
||||
'#dc3545', // Zone 5: red
|
||||
];
|
||||
return colors[index] || '#000';
|
||||
}
|
||||
|
||||
function getHrBarChartData() {
|
||||
const zones = Object.values(hrZones.value);
|
||||
return {
|
||||
labels: zones.map((z, i) => `${t('activitySummaryComponent.activityHRZone')} ${i + 1} (${z.hr || ''})`),
|
||||
// values: zones.map(z => `${z.percent ?? 0}%`),
|
||||
values: zones.map(z => z.percent ?? 0),
|
||||
barColors: zones.map((_, i) => getZoneColor(i)),
|
||||
};
|
||||
const zones = Object.values(hrZones.value);
|
||||
return {
|
||||
labels: zones.map((z, i) => `${t('activitySummaryComponent.activityHRZone')} ${i + 1} (${z.hr || ''})`),
|
||||
// values: zones.map(z => `${z.percent ?? 0}%`),
|
||||
values: zones.map(z => z.percent ?? 0),
|
||||
barColors: zones.map((_, i) => getZoneColor(i)),
|
||||
};
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user