Merge pull request #465 from DavidHenryThoreau/css
Add colors-dark.scss
This commit is contained in:
commit
d207beb78f
4
fittrackee/dist/index.html
vendored
4
fittrackee/dist/index.html
vendored
@ -7,11 +7,11 @@
|
|||||||
<link rel="stylesheet" href="/static/css/fork-awesome.min.css"/>
|
<link rel="stylesheet" href="/static/css/fork-awesome.min.css"/>
|
||||||
<link rel="stylesheet" href="/static/css/leaflet.css"/>
|
<link rel="stylesheet" href="/static/css/leaflet.css"/>
|
||||||
<title>FitTrackee</title>
|
<title>FitTrackee</title>
|
||||||
<script type="module" crossorigin src="/static/index-0EDbp-Wc.js"></script>
|
<script type="module" crossorigin src="/static/index-0v1Cinoq.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/static/charts-_RwsDDkL.js">
|
<link rel="modulepreload" crossorigin href="/static/charts-_RwsDDkL.js">
|
||||||
<link rel="modulepreload" crossorigin href="/static/maps-ZyuCPqes.js">
|
<link rel="modulepreload" crossorigin href="/static/maps-ZyuCPqes.js">
|
||||||
<link rel="stylesheet" crossorigin href="/static/css/maps-B7qTrBCW.css">
|
<link rel="stylesheet" crossorigin href="/static/css/maps-B7qTrBCW.css">
|
||||||
<link rel="stylesheet" crossorigin href="/static/css/index-kyYSvvW8.css">
|
<link rel="stylesheet" crossorigin href="/static/css/index-86bdJKFy.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
1
fittrackee/dist/static/css/index-86bdJKFy.css
vendored
Normal file
1
fittrackee/dist/static/css/index-86bdJKFy.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
457
fittrackee/dist/static/index-0EDbp-Wc.js
vendored
457
fittrackee/dist/static/index-0EDbp-Wc.js
vendored
File diff suppressed because one or more lines are too long
457
fittrackee/dist/static/index-0v1Cinoq.js
vendored
Normal file
457
fittrackee/dist/static/index-0v1Cinoq.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,34 @@
|
|||||||
|
"""add dark theme preferences
|
||||||
|
|
||||||
|
Revision ID: 14f48e46f320
|
||||||
|
Revises: 24eb097614e4
|
||||||
|
Create Date: 2023-12-16 18:35:31.377007
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '14f48e46f320'
|
||||||
|
down_revision = '24eb097614e4'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(
|
||||||
|
sa.Column('use_dark_mode', sa.Boolean(), nullable=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||||
|
batch_op.drop_column('use_dark_mode')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
@ -1459,6 +1459,7 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
|
|||||||
imperial_units=True,
|
imperial_units=True,
|
||||||
display_ascent=False,
|
display_ascent=False,
|
||||||
start_elevation_at_zero=False,
|
start_elevation_at_zero=False,
|
||||||
|
use_dark_mode=True,
|
||||||
use_raw_gpx_speed=True,
|
use_raw_gpx_speed=True,
|
||||||
date_format='yyyy-MM-dd',
|
date_format='yyyy-MM-dd',
|
||||||
)
|
)
|
||||||
@ -1478,6 +1479,7 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
|
|||||||
assert data['data']['timezone'] == 'America/New_York'
|
assert data['data']['timezone'] == 'America/New_York'
|
||||||
assert data['data']['date_format'] == 'yyyy-MM-dd'
|
assert data['data']['date_format'] == 'yyyy-MM-dd'
|
||||||
assert data['data']['weekm'] is True
|
assert data['data']['weekm'] is True
|
||||||
|
assert data['data']['use_dark_mode'] is True
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'client_scope, can_access',
|
'client_scope, can_access',
|
||||||
|
@ -77,6 +77,12 @@ class TestUserSerializeAsAuthUser(UserModelAssertMixin):
|
|||||||
assert serialized_user['timezone'] == user_1.timezone
|
assert serialized_user['timezone'] == user_1.timezone
|
||||||
assert serialized_user['weekm'] == user_1.weekm
|
assert serialized_user['weekm'] == user_1.weekm
|
||||||
assert serialized_user['display_ascent'] == user_1.display_ascent
|
assert serialized_user['display_ascent'] == user_1.display_ascent
|
||||||
|
assert (
|
||||||
|
serialized_user['start_elevation_at_zero']
|
||||||
|
== user_1.start_elevation_at_zero
|
||||||
|
)
|
||||||
|
assert serialized_user['use_raw_gpx_speed'] == user_1.use_raw_gpx_speed
|
||||||
|
assert serialized_user['use_dark_mode'] == user_1.use_dark_mode
|
||||||
|
|
||||||
def test_it_returns_workouts_infos(self, app: Flask, user_1: User) -> None:
|
def test_it_returns_workouts_infos(self, app: Flask, user_1: User) -> None:
|
||||||
serialized_user = user_1.serialize(user_1)
|
serialized_user = user_1.serialize(user_1)
|
||||||
@ -155,6 +161,9 @@ class TestUserSerializeAsAdmin(UserModelAssertMixin):
|
|||||||
assert 'language' not in serialized_user
|
assert 'language' not in serialized_user
|
||||||
assert 'timezone' not in serialized_user
|
assert 'timezone' not in serialized_user
|
||||||
assert 'weekm' not in serialized_user
|
assert 'weekm' not in serialized_user
|
||||||
|
assert 'start_elevation_at_zero' not in serialized_user
|
||||||
|
assert 'use_raw_gpx_speed' not in serialized_user
|
||||||
|
assert 'use_dark_mode' not in serialized_user
|
||||||
|
|
||||||
def test_it_returns_workouts_infos(
|
def test_it_returns_workouts_infos(
|
||||||
self, app: Flask, user_1_admin: User, user_2: User
|
self, app: Flask, user_1_admin: User, user_2: User
|
||||||
|
@ -361,6 +361,7 @@ def get_authenticated_user_profile(
|
|||||||
"total_ascent": 720.35,
|
"total_ascent": 720.35,
|
||||||
"total_distance": 67.895,
|
"total_distance": 67.895,
|
||||||
"total_duration": "6:50:27",
|
"total_duration": "6:50:27",
|
||||||
|
"use_dark_mode": null,
|
||||||
"use_raw_gpx_speed": false,
|
"use_raw_gpx_speed": false,
|
||||||
"username": "sam",
|
"username": "sam",
|
||||||
"weekm": false
|
"weekm": false
|
||||||
@ -478,6 +479,7 @@ def edit_user(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
"total_ascent": 720.35,
|
"total_ascent": 720.35,
|
||||||
"total_distance": 67.895,
|
"total_distance": 67.895,
|
||||||
"total_duration": "6:50:27",
|
"total_duration": "6:50:27",
|
||||||
|
"use_dark_mode": null,
|
||||||
"use_raw_gpx_speed": false,
|
"use_raw_gpx_speed": false,
|
||||||
"username": "sam"
|
"username": "sam"
|
||||||
"weekm": true,
|
"weekm": true,
|
||||||
@ -650,6 +652,7 @@ def update_user_account(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
"total_ascent": 720.35,
|
"total_ascent": 720.35,
|
||||||
"total_distance": 67.895,
|
"total_distance": 67.895,
|
||||||
"total_duration": "6:50:27",
|
"total_duration": "6:50:27",
|
||||||
|
"use_dark_mode": null,
|
||||||
"use_raw_gpx_speed": false,
|
"use_raw_gpx_speed": false,
|
||||||
"username": "sam"
|
"username": "sam"
|
||||||
"weekm": true,
|
"weekm": true,
|
||||||
@ -878,6 +881,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
"total_ascent": 720.35,
|
"total_ascent": 720.35,
|
||||||
"total_distance": 67.895,
|
"total_distance": 67.895,
|
||||||
"total_duration": "6:50:27",
|
"total_duration": "6:50:27",
|
||||||
|
"use_dark_mode": null,
|
||||||
"use_raw_gpx_speed": true,
|
"use_raw_gpx_speed": true,
|
||||||
"username": "sam"
|
"username": "sam"
|
||||||
"weekm": true,
|
"weekm": true,
|
||||||
@ -892,6 +896,8 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
:<json string language: language preferences
|
:<json string language: language preferences
|
||||||
:<json boolean start_elevation_at_zero: do elevation plots start at zero?
|
:<json boolean start_elevation_at_zero: do elevation plots start at zero?
|
||||||
:<json string timezone: user time zone
|
:<json string timezone: user time zone
|
||||||
|
:<json boolean use_dark_mode: Display interface with dark mode if true.
|
||||||
|
If null, it uses browser preferences.
|
||||||
:<json boolean use_raw_gpx_speed: Use unfiltered gpx to calculate speeds
|
:<json boolean use_raw_gpx_speed: Use unfiltered gpx to calculate speeds
|
||||||
:<json boolean weekm: does week start on Monday?
|
:<json boolean weekm: does week start on Monday?
|
||||||
|
|
||||||
@ -916,6 +922,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
'language',
|
'language',
|
||||||
'start_elevation_at_zero',
|
'start_elevation_at_zero',
|
||||||
'timezone',
|
'timezone',
|
||||||
|
'use_dark_mode',
|
||||||
'use_raw_gpx_speed',
|
'use_raw_gpx_speed',
|
||||||
'weekm',
|
'weekm',
|
||||||
}
|
}
|
||||||
@ -928,6 +935,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
language = get_language(post_data.get('language'))
|
language = get_language(post_data.get('language'))
|
||||||
start_elevation_at_zero = post_data.get('start_elevation_at_zero')
|
start_elevation_at_zero = post_data.get('start_elevation_at_zero')
|
||||||
use_raw_gpx_speed = post_data.get('use_raw_gpx_speed')
|
use_raw_gpx_speed = post_data.get('use_raw_gpx_speed')
|
||||||
|
use_dark_mode = post_data.get('use_dark_mode')
|
||||||
timezone = post_data.get('timezone')
|
timezone = post_data.get('timezone')
|
||||||
weekm = post_data.get('weekm')
|
weekm = post_data.get('weekm')
|
||||||
|
|
||||||
@ -938,6 +946,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
auth_user.language = language
|
auth_user.language = language
|
||||||
auth_user.start_elevation_at_zero = start_elevation_at_zero
|
auth_user.start_elevation_at_zero = start_elevation_at_zero
|
||||||
auth_user.timezone = timezone
|
auth_user.timezone = timezone
|
||||||
|
auth_user.use_dark_mode = use_dark_mode
|
||||||
auth_user.use_raw_gpx_speed = use_raw_gpx_speed
|
auth_user.use_raw_gpx_speed = use_raw_gpx_speed
|
||||||
auth_user.weekm = weekm
|
auth_user.weekm = weekm
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -63,6 +63,7 @@ class User(BaseModel):
|
|||||||
db.Boolean, default=True, nullable=False
|
db.Boolean, default=True, nullable=False
|
||||||
)
|
)
|
||||||
use_raw_gpx_speed = db.Column(db.Boolean, default=False, nullable=False)
|
use_raw_gpx_speed = db.Column(db.Boolean, default=False, nullable=False)
|
||||||
|
use_dark_mode = db.Column(db.Boolean, default=False, nullable=True)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'<User {self.username!r}>'
|
return f'<User {self.username!r}>'
|
||||||
@ -217,6 +218,7 @@ class User(BaseModel):
|
|||||||
'language': self.language,
|
'language': self.language,
|
||||||
'start_elevation_at_zero': self.start_elevation_at_zero,
|
'start_elevation_at_zero': self.start_elevation_at_zero,
|
||||||
'timezone': self.timezone,
|
'timezone': self.timezone,
|
||||||
|
'use_dark_mode': self.use_dark_mode,
|
||||||
'use_raw_gpx_speed': self.use_raw_gpx_speed,
|
'use_raw_gpx_speed': self.use_raw_gpx_speed,
|
||||||
'weekm': self.weekm,
|
'weekm': self.weekm,
|
||||||
},
|
},
|
||||||
|
@ -123,7 +123,7 @@
|
|||||||
.scroll-button {
|
.scroll-button {
|
||||||
background-color: var(--scroll-button-bg-color);
|
background-color: var(--scroll-button-bg-color);
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
box-shadow: 1px 1px 3px lightgrey;
|
box-shadow: 1px 1px 3px var(--app-shadow-color);
|
||||||
display: none;
|
display: none;
|
||||||
padding: 0 $default-padding;
|
padding: 0 $default-padding;
|
||||||
|
|
||||||
|
@ -70,14 +70,14 @@
|
|||||||
|
|
||||||
.dropdown-list {
|
.dropdown-list {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
background-color: #ffffff;
|
background-color: var(--dropdown-background-color);
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
margin-left: -20px !important;
|
margin-left: -20px !important;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border: solid 1px lightgrey;
|
border: solid 1px var(--dropdown-border-color);
|
||||||
box-shadow: 2px 2px 5px lightgrey;
|
box-shadow: 2px 2px 5px var(--app-shadow-color);
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
|
@ -51,6 +51,12 @@
|
|||||||
width: 400px;
|
width: 400px;
|
||||||
height: 225px;
|
height: 225px;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
filter: var(--map-display-hover-filter);
|
||||||
|
|
||||||
|
.map-attribution-text {
|
||||||
|
color: var(--map-display-hover-attribution-text);
|
||||||
|
background-color: var(--map-attribution-bg-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-map-image {
|
.bg-map-image {
|
||||||
@ -59,6 +65,7 @@
|
|||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
filter: var(--map-filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-attribution {
|
.map-attribution {
|
||||||
@ -69,7 +76,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.map-attribution-text {
|
.map-attribution-text {
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
color: var(--map-attribution-text);
|
||||||
|
background-color: var(--map-attribution-bg-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -6,13 +6,16 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ChartOptions, LayoutItem } from 'chart.js'
|
import type { ChartOptions, LayoutItem } from 'chart.js'
|
||||||
import { computed, toRefs } from 'vue'
|
import { computed, type ComputedRef, toRefs } from 'vue'
|
||||||
import { Bar } from 'vue-chartjs'
|
import { Bar } from 'vue-chartjs'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
|
||||||
|
import { ROOT_STORE } from '@/store/constants'
|
||||||
import type { IChartDataset } from '@/types/chart'
|
import type { IChartDataset } from '@/types/chart'
|
||||||
import type { TStatisticsDatasetKeys } from '@/types/statistics'
|
import type { TStatisticsDatasetKeys } from '@/types/statistics'
|
||||||
import { formatTooltipValue } from '@/utils/tooltip'
|
import { formatTooltipValue } from '@/utils/tooltip'
|
||||||
|
import { chartsColors } from '@/utils/workouts'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
datasets: IChartDataset[]
|
datasets: IChartDataset[]
|
||||||
@ -32,8 +35,23 @@
|
|||||||
useImperialUnits,
|
useImperialUnits,
|
||||||
} = toRefs(props)
|
} = toRefs(props)
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const darkMode: ComputedRef<boolean | null> = computed(
|
||||||
|
() => store.getters[ROOT_STORE.GETTERS.DARK_MODE]
|
||||||
|
)
|
||||||
|
const lineColors = computed(() => ({
|
||||||
|
color: darkMode.value
|
||||||
|
? chartsColors.darkMode.line
|
||||||
|
: chartsColors.ligthMode.line,
|
||||||
|
}))
|
||||||
|
const textColors = computed(() => ({
|
||||||
|
color: darkMode.value
|
||||||
|
? chartsColors.darkMode.text
|
||||||
|
: chartsColors.ligthMode.text,
|
||||||
|
}))
|
||||||
|
|
||||||
const chartData = computed(() => ({
|
const chartData = computed(() => ({
|
||||||
labels: labels.value,
|
labels: labels.value,
|
||||||
// workaround to avoid dataset modification
|
// workaround to avoid dataset modification
|
||||||
@ -53,12 +71,23 @@
|
|||||||
stacked: true,
|
stacked: true,
|
||||||
grid: {
|
grid: {
|
||||||
drawOnChartArea: false,
|
drawOnChartArea: false,
|
||||||
|
...lineColors.value,
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
...lineColors.value,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
...textColors.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
stacked: displayedData.value !== 'average_speed',
|
stacked: displayedData.value !== 'average_speed',
|
||||||
grid: {
|
grid: {
|
||||||
drawOnChartArea: false,
|
drawOnChartArea: false,
|
||||||
|
...lineColors.value,
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
...lineColors.value,
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
maxTicksLimit: 6,
|
maxTicksLimit: 6,
|
||||||
@ -71,6 +100,7 @@
|
|||||||
getUnit(displayedData.value)
|
getUnit(displayedData.value)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
...textColors.value,
|
||||||
},
|
},
|
||||||
afterFit: function (scale: LayoutItem) {
|
afterFit: function (scale: LayoutItem) {
|
||||||
scale.width = fullStats.value ? 90 : 60
|
scale.width = fullStats.value ? 90 : 60
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
padding: $default-padding * 0.5;
|
padding: $default-padding * 0.5;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--app-color-light);
|
color: var(--calendar-day-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -88,11 +88,11 @@
|
|||||||
padding-left: 40px;
|
padding-left: 40px;
|
||||||
|
|
||||||
.more-workouts {
|
.more-workouts {
|
||||||
background: whitesmoke;
|
background: var(--calendar-workouts-color);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 4px 8px 0 rgba(0, 0, 0, 0.2),
|
0 4px 8px 0 var(--calendar-workouts-box-shadow-0),
|
||||||
0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
0 6px 20px 0 var(--calendar-workouts-box-shadow-1);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 52px;
|
top: 52px;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -60,12 +60,12 @@
|
|||||||
{{ authUser.username }}
|
{{ authUser.username }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<button
|
<button
|
||||||
class="logout-button transparent"
|
class="nav-button logout-button transparent"
|
||||||
@click="updateDisplayModal(true)"
|
@click="updateDisplayModal(true)"
|
||||||
:aria-label="$t('user.LOGOUT')"
|
:title="$t('user.LOGOUT')"
|
||||||
>
|
>
|
||||||
<i class="fa fa-sign-out logout-fa" aria-hidden="true" />
|
<i class="fa fa-sign-out nav-button-fa" aria-hidden="true" />
|
||||||
<span class="logout-text">{{ $t('user.LOGOUT') }}</span>
|
<span class="nav-button-text">{{ $t('user.LOGOUT') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-items-group" v-else>
|
<div class="nav-items-group" v-else>
|
||||||
@ -76,6 +76,27 @@
|
|||||||
{{ $t('user.REGISTER') }}
|
{{ $t('user.REGISTER') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="theme-button">
|
||||||
|
<button
|
||||||
|
class="nav-button transparent"
|
||||||
|
@click="toggleTheme"
|
||||||
|
:title="$t('user.TOGGLE_THEME')"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
v-if="darkTheme"
|
||||||
|
class="fa nav-button-fa fa-moon"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
v-else
|
||||||
|
class="clear-theme"
|
||||||
|
src="/img/weather/clear-day.svg"
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<span class="nav-button-text">{{ $t('user.TOGGLE_THEME') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
v-if="availableLanguages && language"
|
v-if="availableLanguages && language"
|
||||||
class="nav-item"
|
class="nav-item"
|
||||||
@ -93,7 +114,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, capitalize } from 'vue'
|
import { computed, ref, capitalize, onBeforeMount, watch } from 'vue'
|
||||||
import type { ComputedRef, Ref } from 'vue'
|
import type { ComputedRef, Ref } from 'vue'
|
||||||
|
|
||||||
import UserPicture from '@/components/User/UserPicture.vue'
|
import UserPicture from '@/components/User/UserPicture.vue'
|
||||||
@ -119,6 +140,12 @@
|
|||||||
)
|
)
|
||||||
const isMenuOpen: Ref<boolean> = ref(false)
|
const isMenuOpen: Ref<boolean> = ref(false)
|
||||||
const displayModal: Ref<boolean> = ref(false)
|
const displayModal: Ref<boolean> = ref(false)
|
||||||
|
const darkMode: ComputedRef<boolean | null> = computed(
|
||||||
|
() => store.getters[ROOT_STORE.GETTERS.DARK_MODE]
|
||||||
|
)
|
||||||
|
const darkTheme: ComputedRef<boolean> = computed(() => getDarkTheme())
|
||||||
|
|
||||||
|
onBeforeMount(() => setTheme())
|
||||||
|
|
||||||
function openMenu() {
|
function openMenu() {
|
||||||
isMenuOpen.value = true
|
isMenuOpen.value = true
|
||||||
@ -141,6 +168,32 @@
|
|||||||
function updateDisplayModal(display: boolean) {
|
function updateDisplayModal(display: boolean) {
|
||||||
displayModal.value = display
|
displayModal.value = display
|
||||||
}
|
}
|
||||||
|
function getDarkTheme() {
|
||||||
|
if (
|
||||||
|
darkMode.value === null &&
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return darkMode.value === true
|
||||||
|
}
|
||||||
|
function setTheme() {
|
||||||
|
if (darkTheme.value) {
|
||||||
|
document.body.setAttribute('data-theme', 'dark')
|
||||||
|
} else {
|
||||||
|
document.body.removeAttribute('data-theme')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function toggleTheme() {
|
||||||
|
store.commit(ROOT_STORE.MUTATIONS.UPDATE_DARK_MODE, !darkTheme.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => darkTheme.value,
|
||||||
|
() => {
|
||||||
|
setTheme()
|
||||||
|
}
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -252,16 +305,22 @@
|
|||||||
.nav-separator {
|
.nav-separator {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.logout-button {
|
.nav-button {
|
||||||
padding: $default-padding * 0.5 $default-padding * 0.75;
|
padding: $default-padding * 0.5 $default-padding * 0.75;
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
.logout-fa {
|
.nav-button-fa {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.logout-text {
|
.nav-button-text {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clear-theme {
|
||||||
|
filter: var(--workout-img-color);
|
||||||
|
height: 20px;
|
||||||
|
margin-bottom: -5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $medium-limit) {
|
@media screen and (max-width: $medium-limit) {
|
||||||
@ -323,15 +382,16 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.logout-button {
|
.nav-button {
|
||||||
padding: $default-padding $default-padding $default-padding
|
padding: $default-padding $default-padding $default-padding
|
||||||
$default-padding * 2.4;
|
$default-padding * 2.4;
|
||||||
color: var(--app-a-color);
|
color: var(--app-a-color);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
.logout-fa {
|
.nav-button-fa {
|
||||||
display: none;
|
display: none;
|
||||||
|
width: 36px;
|
||||||
}
|
}
|
||||||
.logout-text {
|
.nav-button-text {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -361,6 +421,9 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.theme-button {
|
||||||
|
margin-left: $default-padding * 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
<dl>
|
<dl>
|
||||||
<dt>{{ $t('user.PROFILE.LANGUAGE') }}:</dt>
|
<dt>{{ $t('user.PROFILE.LANGUAGE') }}:</dt>
|
||||||
<dd>{{ userLanguage }}</dd>
|
<dd>{{ userLanguage }}</dd>
|
||||||
|
<dt>{{ $t('user.PROFILE.THEME_MODE.LABEL') }}:</dt>
|
||||||
|
<dd>{{ $t(`user.PROFILE.THEME_MODE.VALUES.${darkMode}`) }}</dd>
|
||||||
<dt>{{ $t('user.PROFILE.TIMEZONE') }}:</dt>
|
<dt>{{ $t('user.PROFILE.TIMEZONE') }}:</dt>
|
||||||
<dd>{{ timezone }}</dd>
|
<dd>{{ timezone }}</dd>
|
||||||
<dt>{{ $t('user.PROFILE.DATE_FORMAT') }}:</dt>
|
<dt>{{ $t('user.PROFILE.DATE_FORMAT') }}:</dt>
|
||||||
@ -95,6 +97,13 @@
|
|||||||
const display_ascent = computed(() =>
|
const display_ascent = computed(() =>
|
||||||
props.user.display_ascent ? 'DISPLAYED' : 'HIDDEN'
|
props.user.display_ascent ? 'DISPLAYED' : 'HIDDEN'
|
||||||
)
|
)
|
||||||
|
const darkMode = computed(() =>
|
||||||
|
props.user.use_dark_mode === true
|
||||||
|
? 'DARK'
|
||||||
|
: props.user.use_dark_mode === false
|
||||||
|
? 'LIGHT'
|
||||||
|
: 'DEFAULT'
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -18,6 +18,22 @@
|
|||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="form-items">
|
||||||
|
{{ $t('user.PROFILE.THEME_MODE.LABEL') }}
|
||||||
|
<select
|
||||||
|
id="use_dark_mode"
|
||||||
|
v-model="userForm.use_dark_mode"
|
||||||
|
:disabled="loading"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="mode in useDarkMode"
|
||||||
|
:value="mode.value"
|
||||||
|
:key="mode.label"
|
||||||
|
>
|
||||||
|
{{ $t(`user.PROFILE.THEME_MODE.VALUES.${mode.label}`) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
<label class="form-items">
|
<label class="form-items">
|
||||||
{{ $t('user.PROFILE.TIMEZONE') }}
|
{{ $t('user.PROFILE.TIMEZONE') }}
|
||||||
<TimezoneDropdown
|
<TimezoneDropdown
|
||||||
@ -195,6 +211,7 @@
|
|||||||
weekm: false,
|
weekm: false,
|
||||||
start_elevation_at_zero: false,
|
start_elevation_at_zero: false,
|
||||||
use_raw_gpx_speed: false,
|
use_raw_gpx_speed: false,
|
||||||
|
use_dark_mode: false,
|
||||||
})
|
})
|
||||||
const weekStart = [
|
const weekStart = [
|
||||||
{
|
{
|
||||||
@ -246,6 +263,20 @@
|
|||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
const useDarkMode = [
|
||||||
|
{
|
||||||
|
label: 'DARK',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'DEFAULT',
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'LIGHT',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
const loading = computed(
|
const loading = computed(
|
||||||
() => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]
|
() => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]
|
||||||
)
|
)
|
||||||
@ -279,6 +310,7 @@
|
|||||||
userForm.timezone = user.timezone ? user.timezone : 'Europe/Paris'
|
userForm.timezone = user.timezone ? user.timezone : 'Europe/Paris'
|
||||||
userForm.date_format = user.date_format ? user.date_format : 'dd/MM/yyyy'
|
userForm.date_format = user.date_format ? user.date_format : 'dd/MM/yyyy'
|
||||||
userForm.weekm = user.weekm ? user.weekm : false
|
userForm.weekm = user.weekm ? user.weekm : false
|
||||||
|
userForm.use_dark_mode = user.use_dark_mode
|
||||||
}
|
}
|
||||||
function updateProfile() {
|
function updateProfile() {
|
||||||
store.dispatch(AUTH_USER_STORE.ACTIONS.UPDATE_USER_PREFERENCES, userForm)
|
store.dispatch(AUTH_USER_STORE.ACTIONS.UPDATE_USER_PREFERENCES, userForm)
|
||||||
@ -339,7 +371,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#language,
|
#language,
|
||||||
#date_format {
|
#date_format,
|
||||||
|
#use_dark_mode {
|
||||||
padding: $default-padding * 0.5;
|
padding: $default-padding * 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@
|
|||||||
|
|
||||||
.policy-content {
|
.policy-content {
|
||||||
height: 500px;
|
height: 500px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid var(--policy-border-color);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
margin: $default-margin;
|
margin: $default-margin;
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
|
@ -238,6 +238,7 @@
|
|||||||
height: 150px;
|
height: 150px;
|
||||||
.no-map {
|
.no-map {
|
||||||
line-height: 150px;
|
line-height: 150px;
|
||||||
|
filter: var(--no-map-filter);
|
||||||
}
|
}
|
||||||
::v-deep(.bg-map-image) {
|
::v-deep(.bg-map-image) {
|
||||||
height: 150px;
|
height: 150px;
|
||||||
|
@ -59,8 +59,10 @@
|
|||||||
import type { ComputedRef } from 'vue'
|
import type { ComputedRef } from 'vue'
|
||||||
import { Line } from 'vue-chartjs'
|
import { Line } from 'vue-chartjs'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
|
||||||
import { htmlLegendPlugin } from '@/components/Workout/WorkoutDetail/WorkoutChart/legend'
|
import { htmlLegendPlugin } from '@/components/Workout/WorkoutDetail/WorkoutChart/legend'
|
||||||
|
import { ROOT_STORE } from '@/store/constants'
|
||||||
import type { TUnit } from '@/types/units'
|
import type { TUnit } from '@/types/units'
|
||||||
import type { IAuthUserProfile } from '@/types/user'
|
import type { IAuthUserProfile } from '@/types/user'
|
||||||
import type {
|
import type {
|
||||||
@ -69,7 +71,7 @@
|
|||||||
TCoordinates,
|
TCoordinates,
|
||||||
} from '@/types/workouts'
|
} from '@/types/workouts'
|
||||||
import { units } from '@/utils/units'
|
import { units } from '@/utils/units'
|
||||||
import { getDatasets } from '@/utils/workouts'
|
import { chartsColors, getDatasets } from '@/utils/workouts'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
authUser: IAuthUserProfile
|
authUser: IAuthUserProfile
|
||||||
@ -79,13 +81,22 @@
|
|||||||
|
|
||||||
const emit = defineEmits(['getCoordinates'])
|
const emit = defineEmits(['getCoordinates'])
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const { authUser, workoutData } = toRefs(props)
|
const { authUser, workoutData } = toRefs(props)
|
||||||
|
const darkMode: ComputedRef<boolean | null> = computed(
|
||||||
|
() => store.getters[ROOT_STORE.GETTERS.DARK_MODE]
|
||||||
|
)
|
||||||
const displayDistance = ref(true)
|
const displayDistance = ref(true)
|
||||||
const beginElevationAtZero = ref(authUser.value.start_elevation_at_zero)
|
const beginElevationAtZero = ref(authUser.value.start_elevation_at_zero)
|
||||||
const datasets: ComputedRef<IWorkoutChartData> = computed(() =>
|
const datasets: ComputedRef<IWorkoutChartData> = computed(() =>
|
||||||
getDatasets(workoutData.value.chartData, t, authUser.value.imperial_units)
|
getDatasets(
|
||||||
|
workoutData.value.chartData,
|
||||||
|
t,
|
||||||
|
authUser.value.imperial_units,
|
||||||
|
darkMode.value !== false
|
||||||
|
)
|
||||||
)
|
)
|
||||||
const hasElevation = computed(
|
const hasElevation = computed(
|
||||||
() => datasets.value && datasets.value.datasets.elevation.data.length > 0
|
() => datasets.value && datasets.value.datasets.elevation.data.length > 0
|
||||||
@ -106,6 +117,17 @@
|
|||||||
const coordinates: ComputedRef<TCoordinates[]> = computed(
|
const coordinates: ComputedRef<TCoordinates[]> = computed(
|
||||||
() => datasets.value.coordinates
|
() => datasets.value.coordinates
|
||||||
)
|
)
|
||||||
|
const lineColors = computed(() => ({
|
||||||
|
color: darkMode.value
|
||||||
|
? chartsColors.darkMode.line
|
||||||
|
: chartsColors.ligthMode.line,
|
||||||
|
}))
|
||||||
|
const textColors = computed(() => ({
|
||||||
|
color: darkMode.value
|
||||||
|
? chartsColors.darkMode.text
|
||||||
|
: chartsColors.ligthMode.text,
|
||||||
|
}))
|
||||||
|
|
||||||
const options = computed<ChartOptions<'line'>>(() => ({
|
const options = computed<ChartOptions<'line'>>(() => ({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
@ -119,6 +141,10 @@
|
|||||||
x: {
|
x: {
|
||||||
grid: {
|
grid: {
|
||||||
drawOnChartArea: false,
|
drawOnChartArea: false,
|
||||||
|
...lineColors.value,
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
...lineColors.value,
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
count: 10,
|
count: 10,
|
||||||
@ -127,6 +153,7 @@
|
|||||||
? Number(value).toFixed(2)
|
? Number(value).toFixed(2)
|
||||||
: formatDuration(value)
|
: formatDuration(value)
|
||||||
},
|
},
|
||||||
|
...textColors.value,
|
||||||
},
|
},
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
bounds: 'data',
|
bounds: 'data',
|
||||||
@ -135,16 +162,25 @@
|
|||||||
text: displayDistance.value
|
text: displayDistance.value
|
||||||
? t('workouts.DISTANCE') + ` (${fromKmUnit})`
|
? t('workouts.DISTANCE') + ` (${fromKmUnit})`
|
||||||
: t('workouts.DURATION'),
|
: t('workouts.DURATION'),
|
||||||
|
...textColors.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ySpeed: {
|
ySpeed: {
|
||||||
grid: {
|
grid: {
|
||||||
drawOnChartArea: false,
|
drawOnChartArea: false,
|
||||||
|
...lineColors.value,
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
...lineColors.value,
|
||||||
},
|
},
|
||||||
position: 'left',
|
position: 'left',
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: t('workouts.SPEED') + ` (${fromKmUnit}/h)`,
|
text: t('workouts.SPEED') + ` (${fromKmUnit}/h)`,
|
||||||
|
...textColors.value,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
...textColors.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
yElevation: {
|
yElevation: {
|
||||||
@ -152,11 +188,19 @@
|
|||||||
display: hasElevation.value,
|
display: hasElevation.value,
|
||||||
grid: {
|
grid: {
|
||||||
drawOnChartArea: false,
|
drawOnChartArea: false,
|
||||||
|
...lineColors.value,
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
...lineColors.value,
|
||||||
},
|
},
|
||||||
position: 'right',
|
position: 'right',
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: t('workouts.ELEVATION') + ` (${fromMUnit})`,
|
text: t('workouts.ELEVATION') + ` (${fromMUnit})`,
|
||||||
|
...textColors.value,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
...textColors.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -143,6 +143,9 @@
|
|||||||
.mountains {
|
.mountains {
|
||||||
padding-right: $default-padding * 0.5;
|
padding-right: $default-padding * 0.5;
|
||||||
}
|
}
|
||||||
|
.mountains {
|
||||||
|
filter: var(--mountains-filter);
|
||||||
|
}
|
||||||
|
|
||||||
.workout-data {
|
.workout-data {
|
||||||
padding: $default-padding * 0.5 0;
|
padding: $default-padding * 0.5 0;
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
ref="workoutMap"
|
ref="workoutMap"
|
||||||
@ready="fitBounds(bounds)"
|
@ready="fitBounds(bounds)"
|
||||||
:use-global-leaflet="false"
|
:use-global-leaflet="false"
|
||||||
|
class="map"
|
||||||
>
|
>
|
||||||
<LControlLayers />
|
<LControlLayers />
|
||||||
<LControl
|
<LControl
|
||||||
@ -212,13 +213,23 @@
|
|||||||
}
|
}
|
||||||
.no-map {
|
.no-map {
|
||||||
line-height: 400px;
|
line-height: 400px;
|
||||||
|
filter: var(--no-map-filter);
|
||||||
}
|
}
|
||||||
.map-control {
|
.leaflet-container {
|
||||||
background: #ffffff;
|
.map {
|
||||||
padding: 5px 10px;
|
filter: var(--map-filter);
|
||||||
border: 2px solid #bfc0ab;
|
}
|
||||||
border-radius: 3px;
|
.map-control {
|
||||||
color: #000000;
|
background: var(--map-control-bg-color);
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: 2px solid var(--map-control-border-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
color: var(--map-control-color);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--dropdown-hover-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
::v-deep(.fullscreen) {
|
::v-deep(.fullscreen) {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -354,7 +354,7 @@
|
|||||||
}
|
}
|
||||||
.static-map {
|
.static-map {
|
||||||
display: none;
|
display: none;
|
||||||
box-shadow: 3px 3px 3px 1px lightgrey;
|
box-shadow: 3px 3px 3px 1px var(--workout-static-map-shadow-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.workout-title:hover .static-map {
|
.workout-title:hover .static-map {
|
||||||
|
@ -115,6 +115,14 @@
|
|||||||
"PROFILE": "profile",
|
"PROFILE": "profile",
|
||||||
"SPORTS": "sports"
|
"SPORTS": "sports"
|
||||||
},
|
},
|
||||||
|
"THEME_MODE": {
|
||||||
|
"LABEL": "Theme mode",
|
||||||
|
"VALUES": {
|
||||||
|
"DARK": "Dark",
|
||||||
|
"DEFAULT": "Browser preference",
|
||||||
|
"LIGHT": "Light"
|
||||||
|
}
|
||||||
|
},
|
||||||
"TIMEZONE": "Timezone",
|
"TIMEZONE": "Timezone",
|
||||||
"UNITS": {
|
"UNITS": {
|
||||||
"IMPERIAL": "Imperial system (ft, mi, mph, °F)",
|
"IMPERIAL": "Imperial system (ft, mi, mph, °F)",
|
||||||
@ -136,6 +144,7 @@
|
|||||||
"REVIEW": "review",
|
"REVIEW": "review",
|
||||||
"SHOW_PASSWORD": "show password",
|
"SHOW_PASSWORD": "show password",
|
||||||
"THIS_USER_ACCOUNT_IS_INACTIVE": "This user account is inactive.",
|
"THIS_USER_ACCOUNT_IS_INACTIVE": "This user account is inactive.",
|
||||||
|
"TOGGLE_THEME": "Toggle theme (Light or Dark mode)",
|
||||||
"USERNAME": "Username",
|
"USERNAME": "Username",
|
||||||
"USERNAME_INFO": "3 to 30 characters required, only alphanumeric characters and the underscore character \"_\" allowed.",
|
"USERNAME_INFO": "3 to 30 characters required, only alphanumeric characters and the underscore character \"_\" allowed.",
|
||||||
"USER_PICTURE": "user picture",
|
"USER_PICTURE": "user picture",
|
||||||
|
@ -115,6 +115,14 @@
|
|||||||
"PROFILE": "profil",
|
"PROFILE": "profil",
|
||||||
"SPORTS": "sports"
|
"SPORTS": "sports"
|
||||||
},
|
},
|
||||||
|
"THEME_MODE": {
|
||||||
|
"LABEL": "Thème",
|
||||||
|
"VALUES": {
|
||||||
|
"DARK": "Sombre",
|
||||||
|
"DEFAULT": "Préférence du navigateur",
|
||||||
|
"LIGHT": "Clair"
|
||||||
|
}
|
||||||
|
},
|
||||||
"TIMEZONE": "Fuseau horaire",
|
"TIMEZONE": "Fuseau horaire",
|
||||||
"UNITS": {
|
"UNITS": {
|
||||||
"IMPERIAL": "Système impérial (ft, mi, mph, °F)",
|
"IMPERIAL": "Système impérial (ft, mi, mph, °F)",
|
||||||
@ -136,6 +144,7 @@
|
|||||||
"REVIEW": "accepter",
|
"REVIEW": "accepter",
|
||||||
"SHOW_PASSWORD": "afficher le mot de passe",
|
"SHOW_PASSWORD": "afficher le mot de passe",
|
||||||
"THIS_USER_ACCOUNT_IS_INACTIVE": "Le compte de cet utilisateur est inactif.",
|
"THIS_USER_ACCOUNT_IS_INACTIVE": "Le compte de cet utilisateur est inactif.",
|
||||||
|
"TOGGLE_THEME": "Modifier le thème (Mode Clair ou Sombre)",
|
||||||
"USERNAME": "Nom d'utilisateur",
|
"USERNAME": "Nom d'utilisateur",
|
||||||
"USERNAME_INFO": "3 à 30 caractères requis, seuls les caractères alphanumériques et le caractère _ sont autorisés.",
|
"USERNAME_INFO": "3 à 30 caractères requis, seuls les caractères alphanumériques et le caractère _ sont autorisés.",
|
||||||
"USER_PICTURE": "photo de l'utilisateur",
|
"USER_PICTURE": "photo de l'utilisateur",
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
@import 'colors';
|
@import 'colors';
|
||||||
|
@import 'colors-dark';
|
||||||
@import 'fonts';
|
@import 'fonts';
|
||||||
@import 'vars';
|
@import 'vars';
|
||||||
|
|
||||||
|
html [data-theme='dark'] {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
@ -55,6 +60,7 @@ select {
|
|||||||
background-color: var(--input-bg-color);
|
background-color: var(--input-bg-color);
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
border: solid 1px var(--input-border-color);
|
border: solid 1px var(--input-border-color);
|
||||||
|
color: var(--input-color);
|
||||||
padding: $default-padding;
|
padding: $default-padding;
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
@ -102,13 +108,13 @@ button {
|
|||||||
|
|
||||||
&:disabled,
|
&:disabled,
|
||||||
&.confirm:disabled {
|
&.confirm:disabled {
|
||||||
border-color: transparent;
|
border-color: var(--disabled-border-color);
|
||||||
color: var(--disabled-color);
|
color: var(--disabled-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--app-color);
|
background: var(--button-transparent-hover-color);
|
||||||
color: var(--button-hover-color);
|
color: var(--button-hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +134,7 @@ button {
|
|||||||
background: var(--button-cancel-bg-color);
|
background: var(--button-cancel-bg-color);
|
||||||
color: var(--button-cancel-color);
|
color: var(--button-cancel-color);
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--app-color);
|
background: var(--button-transparent-hover-color);
|
||||||
color: var(--button-hover-color);
|
color: var(--button-hover-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,7 +143,7 @@ button {
|
|||||||
background: var(--button-confirm-bg-color);
|
background: var(--button-confirm-bg-color);
|
||||||
color: var(--button-confirm-color);
|
color: var(--button-confirm-color);
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--app-color);
|
background: var(--button-transparent-hover-color);
|
||||||
color: var(--button-hover-color);
|
color: var(--button-hover-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,7 +200,7 @@ button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.form-info {
|
.form-info {
|
||||||
color: var(--alert-color);
|
color: var(--form-info);
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
margin-top: -0.2 * $default-margin;
|
margin-top: -0.2 * $default-margin;
|
||||||
padding: 0 $default-padding * 1.5;
|
padding: 0 $default-padding * 1.5;
|
||||||
|
113
fittrackee_client/src/scss/colors-dark.scss
Normal file
113
fittrackee_client/src/scss/colors-dark.scss
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
:root [data-theme='dark'] {
|
||||||
|
--dark-blue: #181a1b;
|
||||||
|
--light-grey: #cfd0d0;
|
||||||
|
|
||||||
|
--app-background-color: var(--dark-blue);
|
||||||
|
--app-color: var(--light-grey);
|
||||||
|
--app-color-light: #6f7070;
|
||||||
|
--app-a-color: var(--light-grey);
|
||||||
|
--app-shadow-color: #383d3f;
|
||||||
|
--app-loading-color: #f3f3f3;
|
||||||
|
--app-loading-top-color: var(--app-color);
|
||||||
|
|
||||||
|
--button-hover-color: var(--app-color);
|
||||||
|
--button-transparent-hover-color: #233240;
|
||||||
|
--button-cancel-bg-color: var(--dark-blue);
|
||||||
|
--button-cancel-color: var(--app-color);
|
||||||
|
--button-confirm-bg-color: var(--dark-blue);
|
||||||
|
--button-confirm-color: var(--app-color);
|
||||||
|
--button-danger-bg-color: var(--dark-blue);
|
||||||
|
--button-danger-color: #dc3545;
|
||||||
|
--button-danger-hover-bg-color: #dc3545;
|
||||||
|
--button-danger-hover-color: var(--dark-blue);
|
||||||
|
|
||||||
|
--card-border-color: #494f52;
|
||||||
|
--input-border-color: #494f52;
|
||||||
|
--input-bg-color: var(--dark-blue);
|
||||||
|
--input-color: var(--app-color);
|
||||||
|
--input-error-color: #dc3545;
|
||||||
|
--dropdown-hover-color: #233240;
|
||||||
|
--dropdown-background-color: var(--dark-blue);
|
||||||
|
--dropdown-border-color: var(--input-border-color);
|
||||||
|
--policy-border-color: #ccc;
|
||||||
|
--box-shadow-color: lightgrey;
|
||||||
|
--admin-disabled-input-color: var(--dark-blue);
|
||||||
|
|
||||||
|
--custom-checkbox-border-color: #665f54;
|
||||||
|
--custom-checkbox-checked-bg-color: #575e62;
|
||||||
|
--custom-checkbox-checked-color: #e8e6e3;
|
||||||
|
|
||||||
|
--calendar-border-color: var(--input-border-color);
|
||||||
|
--calendar-week-end-color: #1e2021;
|
||||||
|
--calendar-day-color: var(--app-color);
|
||||||
|
--calendar-today-color: #202324;
|
||||||
|
--calendar-workouts-color: #233240;
|
||||||
|
--calendar-workouts-box-shadow-0: rgba(0, 0, 0, 0.2);
|
||||||
|
--calendar-workouts-box-shadow-1: rgba(0, 0, 0, 0.19);
|
||||||
|
|
||||||
|
--modal-background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
--nav-bar-background-color: var(--dark-blue);
|
||||||
|
--nav-bar-link-active: #ffffff;
|
||||||
|
--nav-border-color: var(--input-border-color);
|
||||||
|
|
||||||
|
--mobile-menu-selected-color: var(--dark-blue);
|
||||||
|
--mobile-menu-selected-bgcolor: #9da3af;
|
||||||
|
|
||||||
|
--footer-background-color: var(--dark-blue);
|
||||||
|
--footer-border-color: var(--input-border-color);
|
||||||
|
--footer-color: #9f968a;
|
||||||
|
|
||||||
|
--form-info: var(--app-color);
|
||||||
|
|
||||||
|
--alert-background-color: #d6dde3;
|
||||||
|
--alert-color: #3f3f3f;
|
||||||
|
--info-background-color: #33353a;
|
||||||
|
--info-color: var(--app-color);
|
||||||
|
--error-background-color: #4e0000;
|
||||||
|
--error-color: #ea464f;
|
||||||
|
--success-background-color: #24391c;
|
||||||
|
--success-color: #97cd97;
|
||||||
|
|
||||||
|
--disabled-background-color: var(--dark-blue);
|
||||||
|
--disabled-border-color: transparent;
|
||||||
|
--disabled-color: #727272;
|
||||||
|
--disabled-sport-color: #616161;
|
||||||
|
|
||||||
|
--scroll-button-bg-color: var(--dark-blue);
|
||||||
|
|
||||||
|
--workout-trophy-color: #daa520;
|
||||||
|
--workout-img-color: invert(22%) sepia(25%) saturate(646%) hue-rotate(169deg)
|
||||||
|
brightness(97%) contrast(96%);
|
||||||
|
--workout-no-map-bg-color: #eaeaea;
|
||||||
|
--workout-no-map-color: #585959;
|
||||||
|
--map-control-color: #000000;
|
||||||
|
--map-control-bg-color: #ffffff;
|
||||||
|
--map-control-border-color: #bfc0ab;
|
||||||
|
--map-attribution-text: #e8e8e8;
|
||||||
|
--map-display-hover-attribution-text: #444444;
|
||||||
|
--map-attribution-bg-color: none;
|
||||||
|
--map-filter: invert(1) hue-rotate(180deg) brightness(0.8) contrast(0.8);
|
||||||
|
--map-display-hover-filter: invert(1) hue-rotate(180deg) brightness(1.5)
|
||||||
|
contrast(0.6);
|
||||||
|
--map-layers-overlays: var(--app-color);
|
||||||
|
--map-control-bar: var(--app-color);
|
||||||
|
--no-map-filter: invert(1) brightness(1.5) contrast(0.9);
|
||||||
|
--workout-static-map-shadow-color: #d2d2d2;
|
||||||
|
|
||||||
|
--mountains-filter: invert(90%) sepia(19%) saturate(0%) hue-rotate(39deg)
|
||||||
|
brightness(86%) contrast(102%);
|
||||||
|
|
||||||
|
--cell-heading-bg-color: #383838;
|
||||||
|
--cell-heading-color: #eeeeee;
|
||||||
|
|
||||||
|
--svg-filter: drop-shadow(10px 10px 10px var(--app-shadow-color));
|
||||||
|
|
||||||
|
--password-bg-color: #d7dadf;
|
||||||
|
--password-color-weak: #831819;
|
||||||
|
--password-color-medium: #9e6906;
|
||||||
|
--password-color-good: #4b5826;
|
||||||
|
--password-color-strong: #4a8c32;
|
||||||
|
|
||||||
|
--scroll-thumb-color: #949697;
|
||||||
|
}
|
@ -8,6 +8,7 @@
|
|||||||
--app-loading-top-color: var(--app-color);
|
--app-loading-top-color: var(--app-color);
|
||||||
|
|
||||||
--button-hover-color: #ffffff;
|
--button-hover-color: #ffffff;
|
||||||
|
--button-transparent-hover-color: var(--app-color);
|
||||||
--button-cancel-bg-color: #ffffff;
|
--button-cancel-bg-color: #ffffff;
|
||||||
--button-cancel-color: var(--app-color);
|
--button-cancel-color: var(--app-color);
|
||||||
--button-confirm-bg-color: #ffffff;
|
--button-confirm-bg-color: #ffffff;
|
||||||
@ -20,8 +21,14 @@
|
|||||||
--card-border-color: #c4c7cf;
|
--card-border-color: #c4c7cf;
|
||||||
--input-border-color: #9da3af;
|
--input-border-color: #9da3af;
|
||||||
--input-bg-color: #ffffff;
|
--input-bg-color: #ffffff;
|
||||||
|
--input-color: var(--app-color);
|
||||||
--input-error-color: #dc3545;
|
--input-error-color: #dc3545;
|
||||||
--dropdown-hover-color: #eff0f5;
|
--dropdown-hover-color: #eff0f5;
|
||||||
|
--dropdown-background-color: #ffffff;
|
||||||
|
--dropdown-border-color: lightgrey;
|
||||||
|
--policy-border-color: #ccc;
|
||||||
|
--box-shadow-color: lightgrey;
|
||||||
|
--admin-disabled-input-color: #ffffff;
|
||||||
|
|
||||||
--custom-checkbox-border-color: #6d797a;
|
--custom-checkbox-border-color: #6d797a;
|
||||||
--custom-checkbox-checked-bg-color: #6d797a;
|
--custom-checkbox-checked-bg-color: #6d797a;
|
||||||
@ -29,7 +36,11 @@
|
|||||||
|
|
||||||
--calendar-border-color: #c4c7cf;
|
--calendar-border-color: #c4c7cf;
|
||||||
--calendar-week-end-color: #f5f5f5;
|
--calendar-week-end-color: #f5f5f5;
|
||||||
|
--calendar-day-color: var(--app-color-light);
|
||||||
--calendar-today-color: #eff1f3;
|
--calendar-today-color: #eff1f3;
|
||||||
|
--calendar-workouts-color: whitesmoke;
|
||||||
|
--calendar-workouts-box-shadow-0: rgba(0, 0, 0, 0.2);
|
||||||
|
--calendar-workouts-box-shadow-1: rgba(0, 0, 0, 0.19);
|
||||||
|
|
||||||
--modal-background-color: rgba(0, 0, 0, 0.3);
|
--modal-background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
@ -44,6 +55,8 @@
|
|||||||
--footer-border-color: #ebeef3;
|
--footer-border-color: #ebeef3;
|
||||||
--footer-color: #6f7070;
|
--footer-color: #6f7070;
|
||||||
|
|
||||||
|
--form-info: var(--alert-color);
|
||||||
|
|
||||||
--alert-background-color: #d6dde3;
|
--alert-background-color: #d6dde3;
|
||||||
--alert-color: #3f3f3f;
|
--alert-color: #3f3f3f;
|
||||||
--info-background-color: #e5e7ea;
|
--info-background-color: #e5e7ea;
|
||||||
@ -54,6 +67,7 @@
|
|||||||
--success-color: #306430;
|
--success-color: #306430;
|
||||||
|
|
||||||
--disabled-background-color: #e0e0e0;
|
--disabled-background-color: #e0e0e0;
|
||||||
|
--disabled-border-color: transparent;
|
||||||
--disabled-color: #727272;
|
--disabled-color: #727272;
|
||||||
--disabled-sport-color: #616161;
|
--disabled-sport-color: #616161;
|
||||||
|
|
||||||
@ -64,6 +78,21 @@
|
|||||||
brightness(97%) contrast(96%);
|
brightness(97%) contrast(96%);
|
||||||
--workout-no-map-bg-color: #eaeaea;
|
--workout-no-map-bg-color: #eaeaea;
|
||||||
--workout-no-map-color: #585959;
|
--workout-no-map-color: #585959;
|
||||||
|
--map-control-color: #000000;
|
||||||
|
--map-control-bg-color: #ffffff;
|
||||||
|
--map-control-border-color: #bfc0ab;
|
||||||
|
--map-attribution-text: var(--app-color);
|
||||||
|
--map-display-hover-attribution-text: initial;
|
||||||
|
--map-attribution-bg-color: rgba(255, 255, 255, 0.7);
|
||||||
|
--map-filter: initial;
|
||||||
|
--map-display-hover-filter: initial;
|
||||||
|
--map-layers-overlays: initial;
|
||||||
|
--map-control-bar: #bfc0ab;
|
||||||
|
--no-map-filter: initial;
|
||||||
|
--workout-static-map-shadow-color: var(--app-shadow-color);
|
||||||
|
|
||||||
|
--mountains-filter: invert(19%) sepia(9%) saturate(2921%) hue-rotate(169deg)
|
||||||
|
brightness(85%) contrast(80%);
|
||||||
|
|
||||||
--cell-heading-bg-color: #eeeeee;
|
--cell-heading-bg-color: #eeeeee;
|
||||||
--cell-heading-color: #696969;
|
--cell-heading-color: #696969;
|
||||||
|
@ -139,6 +139,10 @@ export const actions: ActionTree<IAuthUserState, IRootState> &
|
|||||||
res.data.data.language
|
res.data.data.language
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
context.commit(
|
||||||
|
ROOT_STORE.MUTATIONS.UPDATE_DARK_MODE,
|
||||||
|
res.data.data.use_dark_mode
|
||||||
|
)
|
||||||
context.dispatch(SPORTS_STORE.ACTIONS.GET_SPORTS)
|
context.dispatch(SPORTS_STORE.ACTIONS.GET_SPORTS)
|
||||||
} else {
|
} else {
|
||||||
handleError(context, null)
|
handleError(context, null)
|
||||||
@ -270,6 +274,10 @@ export const actions: ActionTree<IAuthUserState, IRootState> &
|
|||||||
AUTH_USER_STORE.MUTATIONS.UPDATE_AUTH_USER_PROFILE,
|
AUTH_USER_STORE.MUTATIONS.UPDATE_AUTH_USER_PROFILE,
|
||||||
res.data.data
|
res.data.data
|
||||||
)
|
)
|
||||||
|
context.commit(
|
||||||
|
ROOT_STORE.MUTATIONS.UPDATE_DARK_MODE,
|
||||||
|
res.data.data.use_dark_mode
|
||||||
|
)
|
||||||
context
|
context
|
||||||
.dispatch(
|
.dispatch(
|
||||||
ROOT_STORE.ACTIONS.UPDATE_APPLICATION_LANGUAGE,
|
ROOT_STORE.ACTIONS.UPDATE_APPLICATION_LANGUAGE,
|
||||||
|
@ -10,6 +10,7 @@ export enum RootGetters {
|
|||||||
APP_CONFIG = 'APP_CONFIG',
|
APP_CONFIG = 'APP_CONFIG',
|
||||||
APP_LOADING = 'APP_LOADING',
|
APP_LOADING = 'APP_LOADING',
|
||||||
APP_STATS = 'APP_STATS',
|
APP_STATS = 'APP_STATS',
|
||||||
|
DARK_MODE = 'DARK_MODE',
|
||||||
ERROR_MESSAGES = 'ERROR_MESSAGES',
|
ERROR_MESSAGES = 'ERROR_MESSAGES',
|
||||||
LANGUAGE = 'LANGUAGE',
|
LANGUAGE = 'LANGUAGE',
|
||||||
LOCALE = 'LOCALE', // date-fns
|
LOCALE = 'LOCALE', // date-fns
|
||||||
@ -22,5 +23,6 @@ export enum RootMutations {
|
|||||||
UPDATE_APPLICATION_LOADING = 'UPDATE_APPLICATION_LOADING',
|
UPDATE_APPLICATION_LOADING = 'UPDATE_APPLICATION_LOADING',
|
||||||
UPDATE_APPLICATION_PRIVACY_POLICY = 'UPDATE_APPLICATION_PRIVACY_POLICY',
|
UPDATE_APPLICATION_PRIVACY_POLICY = 'UPDATE_APPLICATION_PRIVACY_POLICY',
|
||||||
UPDATE_APPLICATION_STATS = 'UPDATE_APPLICATION_STATS',
|
UPDATE_APPLICATION_STATS = 'UPDATE_APPLICATION_STATS',
|
||||||
|
UPDATE_DARK_MODE = 'UPDATE_DARK_MODE',
|
||||||
UPDATE_LANG = 'UPDATE_LANG',
|
UPDATE_LANG = 'UPDATE_LANG',
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,9 @@ export const getters: GetterTree<IRootState, IRootState> & IRootGetters = {
|
|||||||
[ROOT_STORE.GETTERS.APP_STATS]: (state: IRootState) => {
|
[ROOT_STORE.GETTERS.APP_STATS]: (state: IRootState) => {
|
||||||
return state.application.statistics
|
return state.application.statistics
|
||||||
},
|
},
|
||||||
|
[ROOT_STORE.GETTERS.DARK_MODE]: (state: IRootState) => {
|
||||||
|
return state.darkMode
|
||||||
|
},
|
||||||
[ROOT_STORE.GETTERS.ERROR_MESSAGES]: (state: IRootState) => {
|
[ROOT_STORE.GETTERS.ERROR_MESSAGES]: (state: IRootState) => {
|
||||||
return state.errorMessages
|
return state.errorMessages
|
||||||
},
|
},
|
||||||
|
@ -45,4 +45,10 @@ export const mutations: MutationTree<IRootState> & TRootMutations = {
|
|||||||
state.language = language
|
state.language = language
|
||||||
state.locale = localeFromLanguage[language]
|
state.locale = localeFromLanguage[language]
|
||||||
},
|
},
|
||||||
|
[ROOT_STORE.MUTATIONS.UPDATE_DARK_MODE](
|
||||||
|
state: IRootState,
|
||||||
|
darkMode: boolean | null
|
||||||
|
) {
|
||||||
|
state.darkMode = darkMode
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -17,4 +17,5 @@ export const state: IRootState = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
appLoading: false,
|
appLoading: false,
|
||||||
|
darkMode: null,
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ export interface IRootState {
|
|||||||
errorMessages: string | string[] | null
|
errorMessages: string | string[] | null
|
||||||
application: IApplication
|
application: IApplication
|
||||||
appLoading: boolean
|
appLoading: boolean
|
||||||
|
darkMode: boolean | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRootActions {
|
export interface IRootActions {
|
||||||
@ -51,6 +52,8 @@ export interface IRootGetters {
|
|||||||
|
|
||||||
[ROOT_STORE.GETTERS.APP_STATS](state: IRootState): IAppStatistics
|
[ROOT_STORE.GETTERS.APP_STATS](state: IRootState): IAppStatistics
|
||||||
|
|
||||||
|
[ROOT_STORE.GETTERS.DARK_MODE](state: IRootState): boolean | null
|
||||||
|
|
||||||
[ROOT_STORE.GETTERS.ERROR_MESSAGES](
|
[ROOT_STORE.GETTERS.ERROR_MESSAGES](
|
||||||
state: IRootState
|
state: IRootState
|
||||||
): string | string[] | null
|
): string | string[] | null
|
||||||
@ -83,6 +86,10 @@ export type TRootMutations<S = IRootState> = {
|
|||||||
statistics: IAppStatistics
|
statistics: IAppStatistics
|
||||||
): void
|
): void
|
||||||
[ROOT_STORE.MUTATIONS.UPDATE_LANG](state: S, language: TLanguage): void
|
[ROOT_STORE.MUTATIONS.UPDATE_LANG](state: S, language: TLanguage): void
|
||||||
|
[ROOT_STORE.MUTATIONS.UPDATE_DARK_MODE](
|
||||||
|
state: S,
|
||||||
|
darkMode: boolean | null
|
||||||
|
): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TRootStoreModule<S = IRootState> = Omit<
|
export type TRootStoreModule<S = IRootState> = Omit<
|
||||||
|
@ -35,6 +35,7 @@ export interface IAuthUserProfile extends IUserProfile {
|
|||||||
timezone: string
|
timezone: string
|
||||||
date_format: string
|
date_format: string
|
||||||
weekm: boolean
|
weekm: boolean
|
||||||
|
use_dark_mode: boolean | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserPayload {
|
export interface IUserPayload {
|
||||||
@ -73,6 +74,7 @@ export interface IUserPreferencesPayload {
|
|||||||
timezone: string
|
timezone: string
|
||||||
date_format: string
|
date_format: string
|
||||||
weekm: boolean
|
weekm: boolean
|
||||||
|
use_dark_mode: boolean | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserSportPreferencesPayload {
|
export interface IUserSportPreferencesPayload {
|
||||||
|
@ -7,24 +7,37 @@ import type {
|
|||||||
} from '@/types/workouts'
|
} from '@/types/workouts'
|
||||||
import { convertStatsDistance } from '@/utils/units'
|
import { convertStatsDistance } from '@/utils/units'
|
||||||
|
|
||||||
|
export const chartsColors = {
|
||||||
|
ligthMode: {
|
||||||
|
// default chartjs values
|
||||||
|
text: '#666',
|
||||||
|
line: 'rgba(0, 0, 0, 0.1)',
|
||||||
|
},
|
||||||
|
darkMode: {
|
||||||
|
text: '#a1a1a1',
|
||||||
|
line: '#3f3f3f',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export const getDatasets = (
|
export const getDatasets = (
|
||||||
chartData: IWorkoutApiChartData[],
|
chartData: IWorkoutApiChartData[],
|
||||||
t: CallableFunction,
|
t: CallableFunction,
|
||||||
useImperialUnits: boolean
|
useImperialUnits: boolean,
|
||||||
|
useDarkMode: boolean = false
|
||||||
): IWorkoutChartData => {
|
): IWorkoutChartData => {
|
||||||
const datasets: TWorkoutDatasets = {
|
const datasets: TWorkoutDatasets = {
|
||||||
speed: {
|
speed: {
|
||||||
label: t('workouts.SPEED'),
|
label: t('workouts.SPEED'),
|
||||||
backgroundColor: ['#FFFFFF'],
|
backgroundColor: ['transparent'],
|
||||||
borderColor: ['#8884d8'],
|
borderColor: [useDarkMode ? '#5f5c97' : '#8884d8'],
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
data: [],
|
data: [],
|
||||||
yAxisID: 'ySpeed',
|
yAxisID: 'ySpeed',
|
||||||
},
|
},
|
||||||
elevation: {
|
elevation: {
|
||||||
label: t('workouts.ELEVATION'),
|
label: t('workouts.ELEVATION'),
|
||||||
backgroundColor: ['#e5e5e5'],
|
backgroundColor: [useDarkMode ? '#303030' : '#e5e5e5'],
|
||||||
borderColor: ['#cccccc'],
|
borderColor: [useDarkMode ? '#222222' : '#cccccc'],
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
fill: true,
|
fill: true,
|
||||||
data: [],
|
data: [],
|
||||||
|
@ -72,8 +72,8 @@
|
|||||||
&:disabled {
|
&:disabled {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield;
|
||||||
background-color: white;
|
background-color: var(--admin-disabled-input-color);
|
||||||
border-color: white;
|
border-color: var(--admin-disabled-input-color);
|
||||||
color: var(--app-color);
|
color: var(--app-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ describe('getDatasets', () => {
|
|||||||
datasets: {
|
datasets: {
|
||||||
speed: {
|
speed: {
|
||||||
label: 'vitesse',
|
label: 'vitesse',
|
||||||
backgroundColor: ['#FFFFFF'],
|
backgroundColor: ['transparent'],
|
||||||
borderColor: ['#8884d8'],
|
borderColor: ['#8884d8'],
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
data: [],
|
data: [],
|
||||||
@ -81,7 +81,7 @@ describe('getDatasets', () => {
|
|||||||
datasets: {
|
datasets: {
|
||||||
speed: {
|
speed: {
|
||||||
label: 'speed',
|
label: 'speed',
|
||||||
backgroundColor: ['#FFFFFF'],
|
backgroundColor: ['transparent'],
|
||||||
borderColor: ['#8884d8'],
|
borderColor: ['#8884d8'],
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
data: [2.89, 20.64, 13.03],
|
data: [2.89, 20.64, 13.03],
|
||||||
@ -145,7 +145,7 @@ describe('getDatasets', () => {
|
|||||||
datasets: {
|
datasets: {
|
||||||
speed: {
|
speed: {
|
||||||
label: 'speed',
|
label: 'speed',
|
||||||
backgroundColor: ['#FFFFFF'],
|
backgroundColor: ['transparent'],
|
||||||
borderColor: ['#8884d8'],
|
borderColor: ['#8884d8'],
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
data: [1.8, 12.83, 8.1],
|
data: [1.8, 12.83, 8.1],
|
||||||
@ -183,6 +183,90 @@ describe('getDatasets', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('getDatasets with dark mode', () => {
|
||||||
|
const testparams = [
|
||||||
|
{
|
||||||
|
description: 'it returns dark mode color',
|
||||||
|
inputParams: {
|
||||||
|
charData: [],
|
||||||
|
locale: 'fr',
|
||||||
|
useImperialUnits: false,
|
||||||
|
useDarkMode: true,
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
distance_labels: [],
|
||||||
|
duration_labels: [],
|
||||||
|
datasets: {
|
||||||
|
speed: {
|
||||||
|
label: 'vitesse',
|
||||||
|
backgroundColor: ['transparent'],
|
||||||
|
borderColor: ['#5f5c97'],
|
||||||
|
borderWidth: 2,
|
||||||
|
data: [],
|
||||||
|
yAxisID: 'ySpeed',
|
||||||
|
},
|
||||||
|
elevation: {
|
||||||
|
label: 'altitude',
|
||||||
|
backgroundColor: ['#303030'],
|
||||||
|
borderColor: ['#222222'],
|
||||||
|
borderWidth: 1,
|
||||||
|
fill: true,
|
||||||
|
data: [],
|
||||||
|
yAxisID: 'yElevation',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
coordinates: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'it returns light mode color',
|
||||||
|
inputParams: {
|
||||||
|
charData: [],
|
||||||
|
locale: 'fr',
|
||||||
|
useImperialUnits: false,
|
||||||
|
useDarkMode: false,
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
distance_labels: [],
|
||||||
|
duration_labels: [],
|
||||||
|
datasets: {
|
||||||
|
speed: {
|
||||||
|
label: 'vitesse',
|
||||||
|
backgroundColor: ['transparent'],
|
||||||
|
borderColor: ['#8884d8'],
|
||||||
|
borderWidth: 2,
|
||||||
|
data: [],
|
||||||
|
yAxisID: 'ySpeed',
|
||||||
|
},
|
||||||
|
elevation: {
|
||||||
|
label: 'altitude',
|
||||||
|
backgroundColor: ['#e5e5e5'],
|
||||||
|
borderColor: ['#cccccc'],
|
||||||
|
borderWidth: 1,
|
||||||
|
fill: true,
|
||||||
|
data: [],
|
||||||
|
yAxisID: 'yElevation',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
coordinates: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
testparams.map((testParams) => {
|
||||||
|
it(testParams.description, () => {
|
||||||
|
locale.value = testParams.inputParams.locale
|
||||||
|
expect(
|
||||||
|
getDatasets(
|
||||||
|
testParams.inputParams.charData,
|
||||||
|
t,
|
||||||
|
testParams.inputParams.useImperialUnits,
|
||||||
|
testParams.inputParams.useDarkMode
|
||||||
|
)
|
||||||
|
).toStrictEqual(testParams.expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('getDonutDatasets', () => {
|
describe('getDonutDatasets', () => {
|
||||||
const testparams = [
|
const testparams = [
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user