From 2b165224eba0302cdaeb26d4aa63ba5462389996 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:12:22 +0000 Subject: [PATCH 01/92] Add modules we will use for importing gear. --- backend/app/gears/gear/router.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/app/gears/gear/router.py b/backend/app/gears/gear/router.py index 922ca2ad7..42d3fc191 100644 --- a/backend/app/gears/gear/router.py +++ b/backend/app/gears/gear/router.py @@ -1,6 +1,6 @@ from typing import Annotated, Callable -from fastapi import APIRouter, Depends, HTTPException, status, Security +from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status, Security from sqlalchemy.orm import Session import session.security as session_security @@ -10,6 +10,14 @@ import gears.gear.crud as gears_crud import gears.gear.dependencies as gears_dependencies import core.database as core_database +import core.logger as core_logger +import core.config as core_config + +import users.user.dependencies as users_dependencies + +import os +import csv +from datetime import datetime # Define the API router router = APIRouter() From e5d873b8dbf90d1bf4d6a4ce41efb4582bd4754d Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:16:01 +0000 Subject: [PATCH 02/92] Adding bike gear importing function based on CSV file. --- backend/app/gears/gear/router.py | 117 +++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/backend/app/gears/gear/router.py b/backend/app/gears/gear/router.py index 42d3fc191..ffb6b20cf 100644 --- a/backend/app/gears/gear/router.py +++ b/backend/app/gears/gear/router.py @@ -271,3 +271,120 @@ async def delete_gear( # Return success message return {"detail": f"Gear ID {gear_id} deleted successfully"} + + +@router.post( + "/stravabikesimport", +) +async def import_bikes_from_Strava_CSV( + token_user_id: Annotated[ + int, + Depends(session_security.get_user_id_from_access_token), + ], + db: Annotated[ + Session, + Depends(core_database.get_db), + ], + background_tasks: BackgroundTasks, +): + import_time_iso = datetime.today().date().strftime('%Y-%m-%d') + #print("import time is: ", import_time_iso) # Testing code + try: + core_logger.print_to_log_and_console(f"Entering import_bikes_from_Strava_CSV backend function") + bulk_import_dir = core_config.FILES_BULK_IMPORT_DIR + + # Hard coding file for now + # Possibly have a version that prompts user for a file. + bikesfilename = "bikes.csv" + + # Get file and parse it + bikes_dict = {} # format: "Bike Name" from the Strava CSV is used as the key, which then holds a dictionary that is based on the Strava bike gear CSV file's data + bikes_file_path = os.path.join(bulk_import_dir, bikesfilename) + try: + if os.path.isfile(bikes_file_path): + core_logger.print_to_log_and_console(f"bikes.csv file exists in bulk_import directory. Starting to process file.") + with open(bikes_file_path, "r") as bike_file: + bikes_csv = csv.DictReader(bike_file) + for row in bikes_csv: # Must process CSV file object while file is still open. + # Example row: {'Bike Name': 'Ox', 'Bike Brand': 'Bianchi', 'Bike Model': 'Advantage', 'Bike Default Sport Types': 'Ride'} + #print("Full row is", row) # Testing code + bikes_dict[row["Bike Name"]] = row + #print("Dicinotary row is: ", bikes_dict[row["Bike Name"]]) # Testing code + #print("Bike brand is: ", bikes_dict[row["Bike Name"]]["Bike Brand"]) # Testing code + core_logger.print_to_log_and_console(f"Strava bike gear csv file parsed and gear dictionary created. File was {len(bikes_dict)} rows long, ignoring header row.") + else: + core_logger.print_to_log_and_console(f"No bikes.csv file located.") + return None # Nothing to return - no file. + except: + # TO DO: RAISE ERROR HERE? + core_logger.print_to_log_and_console(f"Error attempting to open bikes.csv file.") + return None # Nothing to return - error parsing file. + + # Endurain gear schema defined in /backend/app/gears/schema.py + # Relevant functions + # Existing gear for user: gears.crud: get_gear_user(user_id: int, db: Session) -> list[gears_schema.Gear] | None: + # Gear lookup by Strava ID: gears.crud: get_gear_by_strava_id_from_user_id( gear_strava_id: str, user_id: int, db: Session) -> gears_schema.Gear | None: + # Gear create: gears.crud: create_gear(gear: gears_schema.Gear, user_id: int, db: Session): + # Gear edit: edit_gear(gear_id: int, gear: gears_schema.Gear, db: Session): + #print("Getting user gear list") # Testing code + user_gear_list = gears_crud.get_gear_user(token_user_id, db) + #print("Gotten user gear list") # Testing code + #core_logger.print_to_log_and_console(f"User's gear list: {user_gear_list}") # Testing code + if user_gear_list is None: + #User has no gear - we can just add our own straight up. + users_existing_gear_nicknames = None + core_logger.print_to_log_and_console(f"User has no existing gear. Adding all Strava bikes.") # Testing code + else: + #User has gear - we need to check for duplicates. Currently checking by nickname. + core_logger.print_to_log_and_console(f"User has some existing gear. Will only import bikes that are not already present (by checking for nicknames).") # Testing code + users_existing_gear_nicknames = [] + for item in user_gear_list: + #print("Gear item: ", item) # Testing code + #print("ID: ", item.id) # Testing code + #print("Brand: ", item.brand) # Testing code + #print("Model: ", item.model) # Testing code + #print("Nickname: ", item.nickname) # Testing code + #print("Gear type: ", item.gear_type) # Testing code + #print("User: ", item.user_id) # Testing code + #print("Created at: ", item.created_at) # Testing code + #print("is_active: ", item.is_active) # Testing code + #print("strava ID: ", item.strava_gear_id) # Testing code + #print("Gear item listing done.") # Testing code + users_existing_gear_nicknames.append(item.nickname) + #print("User existing gear list is: ", users_existing_gear_nicknames) # Testing code + for bike in bikes_dict: # bike here is the nickname of the bike from Strava (the index of our bikes_dict) + core_logger.print_to_log_and_console(f"In bikes_dict iterator. Current bike is - {bike}") # Testing code. + #print("In bikes_dict iterator - bike brand is: ", bikes_dict[bike]["Bike Brand"]) # Testing code + if bike in users_existing_gear_nicknames: + core_logger.print_to_log_and_console(f"Bike - {bike} - found in existing user gear (nicknames matched). Skipping import.") + else: + core_logger.print_to_log_and_console(f"Bike - {bike} - not found in existing user gear. Importing.") + # Note - hard-coding gear type of bike to be gear_type of 1 here + # There seems to be no single-point list of gear type in the code. + # Best list appears to be at /frontend/app/src/components/Gears/GearsListComponent.vue + # Strava does not export its internal gear ID, so we do not have that information. + # Strava does not export the active / inactive state of the bike, so listing all as active (as we need a status). + new_gear = gears_schema.Gear( + user_id = token_user_id, + brand = bikes_dict[bike]["Bike Brand"], + model = bikes_dict[bike]["Bike Model"], + nickname = bike, + gear_type = 1, + created_at = import_time_iso, + is_active = True, + strava_gear_id = None + ) + gears_crud.create_gear(new_gear, token_user_id, db) + core_logger.print_to_log_and_console(f"Bike - {bike} - has been imported.") + # Return a success message + return {"Gear import successful."} + except Exception as err: + # Log the exception + core_logger.print_to_log_and_console( + f"Error in import_bikes_from_Strava_CSV: {err}", "error" + ) + # Raise an HTTPException with a 500 Internal Server Error status code + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Internal Server Error", + ) from err From 058990395cce4b9df81efbfd21a762302f5fbe43 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:17:56 +0000 Subject: [PATCH 03/92] Creating an import zone for the front end. --- .../Settings/SettingsImportZone.vue | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 frontend/app/src/components/Settings/SettingsImportZone.vue diff --git a/frontend/app/src/components/Settings/SettingsImportZone.vue b/frontend/app/src/components/Settings/SettingsImportZone.vue new file mode 100644 index 000000000..e72272b83 --- /dev/null +++ b/frontend/app/src/components/Settings/SettingsImportZone.vue @@ -0,0 +1,103 @@ + + + From 66435ef1d4a8a535c497a1a9b2c5997133e673bd Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:19:21 +0000 Subject: [PATCH 04/92] Remove import bits from Settings integration zone. --- .../Settings/SettingsIntegrationsZone.vue | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/frontend/app/src/components/Settings/SettingsIntegrationsZone.vue b/frontend/app/src/components/Settings/SettingsIntegrationsZone.vue index 874375804..6d8c5441b 100644 --- a/frontend/app/src/components/Settings/SettingsIntegrationsZone.vue +++ b/frontend/app/src/components/Settings/SettingsIntegrationsZone.vue @@ -60,23 +60,6 @@ - -
  • -
    - -
    -
    - {{ $t("settingsIntegrationsZone.bulkImportIntegrationTitle") }} -
    - {{ $t("settingsIntegrationsZone.bulkImportIntegrationBody") }} -
    -
    - -
  • @@ -282,15 +265,6 @@ async function buttonStravaUnlink() { } } -async function submitBulkImport() { - try { - await activities.bulkImportActivities(); - push.info(t("settingsIntegrationsZone.loadingMessageBulkImport")); - } catch (error) { - push.error(`${t("settingsIntegrationsZone.errorMessageUnableToImportActivities")} - ${error}`); - } -} - async function submitRetrieveGarminConnectActivitiesDays(days) { try { const endDate = new Date(); From 03cb53554f34914e7219965da88537d9eb1812f5 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:20:50 +0000 Subject: [PATCH 05/92] Add import zone to the sidebar navigation. --- .../src/components/Settings/SettingsSideBarComponent.vue | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/app/src/components/Settings/SettingsSideBarComponent.vue b/frontend/app/src/components/Settings/SettingsSideBarComponent.vue index d9f12c8a6..dca857b36 100644 --- a/frontend/app/src/components/Settings/SettingsSideBarComponent.vue +++ b/frontend/app/src/components/Settings/SettingsSideBarComponent.vue @@ -38,6 +38,12 @@ {{ $t("settingsSideBar.integrationsSection") }}
  • + @@ -67,4 +73,4 @@ export default { }; }, }; - \ No newline at end of file + From 73e1603a9596ecc1c4a27ede31df950862e92da7 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:23:03 +0000 Subject: [PATCH 06/92] adding import settings zone to index.js. --- frontend/app/src/i18n/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/app/src/i18n/index.js b/frontend/app/src/i18n/index.js index bedf62630..8aded4e85 100644 --- a/frontend/app/src/i18n/index.js +++ b/frontend/app/src/i18n/index.js @@ -386,6 +386,7 @@ import ptSettingsLanguageSwitcherComponent from './pt/components/settings/settin import ptSettingsUserProfileZoneComponent from './pt/components/settings/settingsUserProfileZoneComponent.json'; import ptSettingsSecurityZoneComponent from './pt/components/settings/settingsSecurityZoneComponent.json'; import ptSettingsIntegrationsZoneComponent from './pt/components/settings/settingsIntegrationsZoneComponent.json'; +import usSettingsImportZoneComponent from './us/components/settings/settingsImportZoneComponent.json'; import ptGarminConnectLoginModalComponent from './pt/components/settings/settingsIntegrationsZone/garminConnectLoginModalComponent.json'; import ptUsersAddEditUserModalComponent from './pt/components/settings/settingsUsersZone/usersAddEditUserModalComponent.json'; import ptUsersChangeUserPasswordModalComponent from './pt/components/settings/settingsUsersZone/usersChangeUserPasswordModalComponent.json'; @@ -933,6 +934,7 @@ const messages = { settingsUserProfileZone: usSettingsUserProfileZoneComponent, settingsSecurityZone: usSettingsSecurityZoneComponent, settingsIntegrationsZone: usSettingsIntegrationsZoneComponent, + settingsImportZone: usSettingsImportZoneComponent, garminConnectLoginModalComponent: usGarminConnectLoginModalComponent, usersAddEditUserModalComponent: usUsersAddEditUserModalComponent, usersChangeUserPasswordModalComponent: usUsersChangeUserPasswordModalComponent, From de51acca653ef80964fd4eb335e5d676885d9588 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:24:08 +0000 Subject: [PATCH 07/92] Adding UI text for import zone buttons. --- .../settings/settingsImportZoneComponent.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json diff --git a/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json new file mode 100644 index 000000000..06ab025bb --- /dev/null +++ b/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json @@ -0,0 +1,13 @@ +{ + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files stored in the files/bulk_import folder", + "buttonBulkImport": "Import", + "stravaImportbuttonActivities": "Activities import", + "loadingMessageBulkImport": "Importing activities", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaImportIntegrationTitle": "Strava import", + "stravaImportIntegrationBody": "Import items from a Strava bulk export (BETA - use with caution)", + "stravaImportbuttonBikes": "Bikes import", + "loadingMessageStravaBikesImport": "Importing bikes", + "errorMessageUnableToImportBikes": "An error occurred while importing Stava bikes" +} From c6bf733baa7dfe1fffa56e85a7c384c7175346e9 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:24:55 +0000 Subject: [PATCH 08/92] removing import text from Integrations zone UI. --- .../settings/settingsIntegrationsZoneComponent.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frontend/app/src/i18n/us/components/settings/settingsIntegrationsZoneComponent.json b/frontend/app/src/i18n/us/components/settings/settingsIntegrationsZoneComponent.json index bee3531de..e381b6950 100644 --- a/frontend/app/src/i18n/us/components/settings/settingsIntegrationsZoneComponent.json +++ b/frontend/app/src/i18n/us/components/settings/settingsIntegrationsZoneComponent.json @@ -27,11 +27,6 @@ "errorMessageUnableToUnlinkStrava": "Unable to unlink Strava account", "modalUnlinkStravaTitle": "Unlink Strava", "modalUnlinkStravaBody": "Are you sure you want to unlink your Strava account? Unlinking your Strava account will remove all your Strava activities and gear from Endurain.", - "bulkImportIntegrationTitle": "Bulk import", - "bulkImportIntegrationBody": "Bulk import activities from files stored in the activity_files/bulk_import folder", - "buttonBulkImport": "Import", - "errorMessageUnableToImportActivities": "An error occurred while importing activities", - "loadingMessageBulkImport": "Importing activities", "garminConnectIntegrationTitle": "Garmin Connect", "garminConnectIntegrationBody": "Garmin Connect is a health and fitness activity platform for users of Garmin devices", "loadingMessageRetrievingGarminConnectActivities": "Retrieving Garmin Connect activities", From d159c10b7f5935a0cdc0a0a785486d1396328735 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:25:40 +0000 Subject: [PATCH 09/92] Adding import zone text to sidebar UI. --- .../us/components/settings/settingsSideBarComponent.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/app/src/i18n/us/components/settings/settingsSideBarComponent.json b/frontend/app/src/i18n/us/components/settings/settingsSideBarComponent.json index 7479a7d7f..1646ee9ee 100644 --- a/frontend/app/src/i18n/us/components/settings/settingsSideBarComponent.json +++ b/frontend/app/src/i18n/us/components/settings/settingsSideBarComponent.json @@ -4,5 +4,6 @@ "generalSection": "General", "myProfileSection": "My Profile", "securitySection": "Security", - "integrationsSection": "Integrations" -} \ No newline at end of file + "integrationsSection": "Integrations", + "importSection": "Import" +} From 2f3ff26734d8e757f03330989b558a847704a045 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:27:19 +0000 Subject: [PATCH 10/92] adding strava bike import call to frontend gears service. --- frontend/app/src/services/gearsService.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/app/src/services/gearsService.js b/frontend/app/src/services/gearsService.js index fd4ada15b..0922c38d7 100644 --- a/frontend/app/src/services/gearsService.js +++ b/frontend/app/src/services/gearsService.js @@ -30,5 +30,8 @@ export const gears = { }, deleteGear(gearId) { return fetchDeleteRequest(`gears/${gearId}`); + }, + stravaBikesImport() { + return fetchPostRequest('gears/stravabikesimport'); } -}; \ No newline at end of file +}; From e42a2f2a85acad820f0419cd683e81194f4663e5 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Mon, 28 Jul 2025 20:29:02 +0000 Subject: [PATCH 11/92] Adding import page to the Settings view. --- frontend/app/src/views/SettingsView.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/app/src/views/SettingsView.vue b/frontend/app/src/views/SettingsView.vue index 5c0bd3578..b2b86195a 100644 --- a/frontend/app/src/views/SettingsView.vue +++ b/frontend/app/src/views/SettingsView.vue @@ -22,6 +22,9 @@ + + + @@ -45,6 +48,7 @@ import SettingsGeneralZone from '../components/Settings/SettingsGeneralZone.vue' import SettingsUserProfileZone from '../components/Settings/SettingsUserProfileZone.vue'; import SettingsSecurityZone from '../components/Settings/SettingsSecurityZone.vue'; import SettingsIntegrationsZone from '../components/Settings/SettingsIntegrationsZone.vue'; +import SettingsImportZone from '../components/Settings/SettingsImportZone.vue'; import BackButtonComponent from '@/components/GeneralComponents/BackButtonComponent.vue'; export default { @@ -56,6 +60,7 @@ export default { SettingsUserProfileZone, SettingsSecurityZone, SettingsIntegrationsZone, + SettingsImportZone, BackButtonComponent, }, setup () { @@ -116,4 +121,4 @@ export default { }; }, }; - \ No newline at end of file + From eebc1905edcf4b641882f8a26a7e89fb436f6235 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Tue, 29 Jul 2025 17:53:20 +0000 Subject: [PATCH 12/92] reformatting import zone vue file to align with v13 style. --- .../Settings/SettingsImportZone.vue | 71 +++++++------------ 1 file changed, 27 insertions(+), 44 deletions(-) diff --git a/frontend/app/src/components/Settings/SettingsImportZone.vue b/frontend/app/src/components/Settings/SettingsImportZone.vue index e72272b83..176752adf 100644 --- a/frontend/app/src/components/Settings/SettingsImportZone.vue +++ b/frontend/app/src/components/Settings/SettingsImportZone.vue @@ -42,7 +42,7 @@ - From 161e5698b03a583aeacadea597851a9a8573b496 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Tue, 29 Jul 2025 17:57:19 +0000 Subject: [PATCH 13/92] Updating documentation to align with new folder structure and include bike import instructions. --- docs/getting-started/advanced-started.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/advanced-started.md b/docs/getting-started/advanced-started.md index 841dcbd45..f82432cff 100644 --- a/docs/getting-started/advanced-started.md +++ b/docs/getting-started/advanced-started.md @@ -80,8 +80,8 @@ Docker image uses a non-root user, so ensure target folders are not owned by roo ## Bulk import and file upload To perform a bulk import: -- Place .fit, .tcx, .gz and/or .gpx files into the activity_files/bulk_import folder. Create the folder if needed. -- In the "Settings" menu select "Integrations". +- Place .fit, .tcx, .gz and/or .gpx files into the data/activity_files/bulk_import folder. Create the folder if needed. +- In the "Settings" menu select "Import". - Click "Import" next to "Bulk Import". .fit files are preferred. I noticed that Strava/Garmin Connect process of converting .fit to .gpx introduces additional data to the activity file leading to minor variances in the data, like for example additional @@ -91,8 +91,22 @@ meters in distance and elevation gain. Some notes: - GEOCODES API has a limit of 1 Request/Second on the free plan, so if you have a large number of files, it might not be possible to import all in the same action - The bulk import currently only imports data present in the .fit, .tcx or .gpx files - no metadata or other media are imported. +## Importing information from a Strava bulk export (BETA) + +Strava allows uesrs to create a bulk export of their historical activity on the site. This information is stored in a zip file, primarily as .csv files, GPS recording files (e.g., .gpx, .fit), and media files (e.g., .jpg, .png). + +At the present time, importing bikes from a Strava bulk export is implemented as a beta feature - use with caution. Components of bikes are not imported - just the bikes themselves. + +To perform an import of bikes: +- Place the bikes.csv file from a Strava bulk export into the data/activity_files/bulk_import folder. Create the folder if needed. +- In the "Settings" menu select "Import". +- Click "Bikes Import" next to "Strava import". +- Status messages about the import, including why any gear was not imported, can be found in the logs. + +Please report any problems observed with bike imports on GitHub. + ## Image personalization It is possible (v0.10.0 or higher) to personalize the login image in the login page. To do that, map the data/server_images directory for image persistence on container updates and: - Set the image in the server settings zone of the settings page - - A square image is expected. Default one uses 1000px vs 1000px \ No newline at end of file + - A square image is expected. Default one uses 1000px vs 1000px From 6cbb65dc8a7f94aa1c2b5bfc675701373d98a59c Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Tue, 29 Jul 2025 18:02:34 +0000 Subject: [PATCH 14/92] Adding specifics of what should be in the bikes.csv file. --- docs/getting-started/advanced-started.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/getting-started/advanced-started.md b/docs/getting-started/advanced-started.md index f82432cff..d6319693b 100644 --- a/docs/getting-started/advanced-started.md +++ b/docs/getting-started/advanced-started.md @@ -103,6 +103,8 @@ To perform an import of bikes: - Click "Bikes Import" next to "Strava import". - Status messages about the import, including why any gear was not imported, can be found in the logs. +Ensure the file is named "bikes.csv" and has a header row with at least the fields 'Bike Name', 'Bike Brand', and 'Bike Model'. + Please report any problems observed with bike imports on GitHub. ## Image personalization From b89c8f8a6e142080fb11d991ab791b9def0785ab Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Tue, 29 Jul 2025 18:06:27 +0000 Subject: [PATCH 15/92] Minor cleanups to router.py --- backend/app/gears/gear/router.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/app/gears/gear/router.py b/backend/app/gears/gear/router.py index ffb6b20cf..9ac204a7f 100644 --- a/backend/app/gears/gear/router.py +++ b/backend/app/gears/gear/router.py @@ -288,18 +288,16 @@ async def import_bikes_from_Strava_CSV( background_tasks: BackgroundTasks, ): import_time_iso = datetime.today().date().strftime('%Y-%m-%d') - #print("import time is: ", import_time_iso) # Testing code try: - core_logger.print_to_log_and_console(f"Entering import_bikes_from_Strava_CSV backend function") + core_logger.print_to_log("Entering bike importing function") bulk_import_dir = core_config.FILES_BULK_IMPORT_DIR - # Hard coding file for now - # Possibly have a version that prompts user for a file. + # Hard coding filename for now (this is the filename Strava uses) bikesfilename = "bikes.csv" # Get file and parse it - bikes_dict = {} # format: "Bike Name" from the Strava CSV is used as the key, which then holds a dictionary that is based on the Strava bike gear CSV file's data bikes_file_path = os.path.join(bulk_import_dir, bikesfilename) + bikes_dict = {} # format: "Bike Name" from the Strava CSV is used as the key, which then holds a dictionary that is based on the Strava bike gear CSV file's data try: if os.path.isfile(bikes_file_path): core_logger.print_to_log_and_console(f"bikes.csv file exists in bulk_import directory. Starting to process file.") From a167458187678e3e5418b57797bb337289fd0ef5 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Tue, 29 Jul 2025 18:15:49 +0000 Subject: [PATCH 16/92] Removing test code and cleaning up comments for gear import. --- backend/app/gears/gear/router.py | 50 +++++++++----------------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/backend/app/gears/gear/router.py b/backend/app/gears/gear/router.py index 9ac204a7f..59d635028 100644 --- a/backend/app/gears/gear/router.py +++ b/backend/app/gears/gear/router.py @@ -290,13 +290,13 @@ async def import_bikes_from_Strava_CSV( import_time_iso = datetime.today().date().strftime('%Y-%m-%d') try: core_logger.print_to_log("Entering bike importing function") - bulk_import_dir = core_config.FILES_BULK_IMPORT_DIR - # Hard coding filename for now (this is the filename Strava uses) - bikesfilename = "bikes.csv" + # CSV file location + bulk_import_dir = core_config.FILES_BULK_IMPORT_DIR + bikesfilename = "bikes.csv" # Hard coding filename for now (this is the filename Strava uses) + bikes_file_path = os.path.join(bulk_import_dir, bikesfilename) # Get file and parse it - bikes_file_path = os.path.join(bulk_import_dir, bikesfilename) bikes_dict = {} # format: "Bike Name" from the Strava CSV is used as the key, which then holds a dictionary that is based on the Strava bike gear CSV file's data try: if os.path.isfile(bikes_file_path): @@ -305,63 +305,40 @@ async def import_bikes_from_Strava_CSV( bikes_csv = csv.DictReader(bike_file) for row in bikes_csv: # Must process CSV file object while file is still open. # Example row: {'Bike Name': 'Ox', 'Bike Brand': 'Bianchi', 'Bike Model': 'Advantage', 'Bike Default Sport Types': 'Ride'} - #print("Full row is", row) # Testing code bikes_dict[row["Bike Name"]] = row - #print("Dicinotary row is: ", bikes_dict[row["Bike Name"]]) # Testing code - #print("Bike brand is: ", bikes_dict[row["Bike Name"]]["Bike Brand"]) # Testing code core_logger.print_to_log_and_console(f"Strava bike gear csv file parsed and gear dictionary created. File was {len(bikes_dict)} rows long, ignoring header row.") else: core_logger.print_to_log_and_console(f"No bikes.csv file located.") return None # Nothing to return - no file. except: - # TO DO: RAISE ERROR HERE? + # TO DO: RAISE ERROR OR ADD NOTIFICATON HERE? core_logger.print_to_log_and_console(f"Error attempting to open bikes.csv file.") return None # Nothing to return - error parsing file. - # Endurain gear schema defined in /backend/app/gears/schema.py - # Relevant functions - # Existing gear for user: gears.crud: get_gear_user(user_id: int, db: Session) -> list[gears_schema.Gear] | None: - # Gear lookup by Strava ID: gears.crud: get_gear_by_strava_id_from_user_id( gear_strava_id: str, user_id: int, db: Session) -> gears_schema.Gear | None: - # Gear create: gears.crud: create_gear(gear: gears_schema.Gear, user_id: int, db: Session): - # Gear edit: edit_gear(gear_id: int, gear: gears_schema.Gear, db: Session): - #print("Getting user gear list") # Testing code + # Get user's existing gear user_gear_list = gears_crud.get_gear_user(token_user_id, db) - #print("Gotten user gear list") # Testing code - #core_logger.print_to_log_and_console(f"User's gear list: {user_gear_list}") # Testing code if user_gear_list is None: #User has no gear - we can just add our own straight up. users_existing_gear_nicknames = None - core_logger.print_to_log_and_console(f"User has no existing gear. Adding all Strava bikes.") # Testing code else: - #User has gear - we need to check for duplicates. Currently checking by nickname. - core_logger.print_to_log_and_console(f"User has some existing gear. Will only import bikes that are not already present (by checking for nicknames).") # Testing code + #User has gear - we will need to check for duplicates. So build a list of gear nicknames to check against. users_existing_gear_nicknames = [] for item in user_gear_list: - #print("Gear item: ", item) # Testing code - #print("ID: ", item.id) # Testing code - #print("Brand: ", item.brand) # Testing code - #print("Model: ", item.model) # Testing code - #print("Nickname: ", item.nickname) # Testing code - #print("Gear type: ", item.gear_type) # Testing code - #print("User: ", item.user_id) # Testing code - #print("Created at: ", item.created_at) # Testing code - #print("is_active: ", item.is_active) # Testing code - #print("strava ID: ", item.strava_gear_id) # Testing code - #print("Gear item listing done.") # Testing code users_existing_gear_nicknames.append(item.nickname) - #print("User existing gear list is: ", users_existing_gear_nicknames) # Testing code + + # Go through bikes and add them to the database if they are not duplicates. for bike in bikes_dict: # bike here is the nickname of the bike from Strava (the index of our bikes_dict) - core_logger.print_to_log_and_console(f"In bikes_dict iterator. Current bike is - {bike}") # Testing code. - #print("In bikes_dict iterator - bike brand is: ", bikes_dict[bike]["Bike Brand"]) # Testing code + #core_logger.print_to_log_and_console(f"In bikes_dict iterator. Current bike is - {bike}") # Testing code. if bike in users_existing_gear_nicknames: core_logger.print_to_log_and_console(f"Bike - {bike} - found in existing user gear (nicknames matched). Skipping import.") else: core_logger.print_to_log_and_console(f"Bike - {bike} - not found in existing user gear. Importing.") - # Note - hard-coding gear type of bike to be gear_type of 1 here + # Notes on the import: + # Hard-coding gear type of bike to be gear_type of 1 here # There seems to be no single-point list of gear type in the code. # Best list appears to be at /frontend/app/src/components/Gears/GearsListComponent.vue # Strava does not export its internal gear ID, so we do not have that information. - # Strava does not export the active / inactive state of the bike, so listing all as active (as we need a status). + # Strava does not export the active / inactive state of the bike, so importing all as active (as we need a status). new_gear = gears_schema.Gear( user_id = token_user_id, brand = bikes_dict[bike]["Bike Brand"], @@ -375,6 +352,7 @@ async def import_bikes_from_Strava_CSV( gears_crud.create_gear(new_gear, token_user_id, db) core_logger.print_to_log_and_console(f"Bike - {bike} - has been imported.") # Return a success message + core_logger.print_to_log_and_console("Bike import complete.") return {"Gear import successful."} except Exception as err: # Log the exception From 1f18cf23c4adf04c899751a33fdafad0c1ffd3cc Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Tue, 29 Jul 2025 18:41:13 +0000 Subject: [PATCH 17/92] Adding check for proper column names in bikes.csv before import. --- backend/app/gears/gear/router.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/app/gears/gear/router.py b/backend/app/gears/gear/router.py index 59d635028..496dd477a 100644 --- a/backend/app/gears/gear/router.py +++ b/backend/app/gears/gear/router.py @@ -305,6 +305,9 @@ async def import_bikes_from_Strava_CSV( bikes_csv = csv.DictReader(bike_file) for row in bikes_csv: # Must process CSV file object while file is still open. # Example row: {'Bike Name': 'Ox', 'Bike Brand': 'Bianchi', 'Bike Model': 'Advantage', 'Bike Default Sport Types': 'Ride'} + if ('Bike Name' not in row) or ('Bike Brand' not in row) or ('Bike Model' not in row): + core_logger.print_to_log_and_console("Aborting bikes import: Proper headers not found in bikes.csv. File should have 'Bike Name', 'Bike Brand', and 'Bike Model'.") + return None bikes_dict[row["Bike Name"]] = row core_logger.print_to_log_and_console(f"Strava bike gear csv file parsed and gear dictionary created. File was {len(bikes_dict)} rows long, ignoring header row.") else: From e535e8e78d016303107e8677c0a2c484a15cd72c Mon Sep 17 00:00:00 2001 From: Marc Perkins <43506722+F-Stop@users.noreply.github.com> Date: Wed, 30 Jul 2025 21:03:58 +0300 Subject: [PATCH 18/92] Fixing typo. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../us/components/settings/settingsImportZoneComponent.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json index 06ab025bb..797ebe882 100644 --- a/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json +++ b/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json @@ -9,5 +9,5 @@ "stravaImportIntegrationBody": "Import items from a Strava bulk export (BETA - use with caution)", "stravaImportbuttonBikes": "Bikes import", "loadingMessageStravaBikesImport": "Importing bikes", - "errorMessageUnableToImportBikes": "An error occurred while importing Stava bikes" + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" } From 9811289ab9fb4934616d67bc74eac4940ea8e3c4 Mon Sep 17 00:00:00 2001 From: Marc Perkins <43506722+F-Stop@users.noreply.github.com> Date: Wed, 30 Jul 2025 21:04:48 +0300 Subject: [PATCH 19/92] Fixing comment. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- frontend/app/src/views/SettingsView.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/src/views/SettingsView.vue b/frontend/app/src/views/SettingsView.vue index b2b86195a..0fcba3265 100644 --- a/frontend/app/src/views/SettingsView.vue +++ b/frontend/app/src/views/SettingsView.vue @@ -23,7 +23,7 @@ - + From 6ac10e2a6fcebb4dc4b571de0bf36db2d72beaa6 Mon Sep 17 00:00:00 2001 From: Marc Perkins <43506722+F-Stop@users.noreply.github.com> Date: Wed, 30 Jul 2025 21:05:43 +0300 Subject: [PATCH 20/92] Fixing typo in docs. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/getting-started/advanced-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/advanced-started.md b/docs/getting-started/advanced-started.md index d6319693b..7afb085f8 100644 --- a/docs/getting-started/advanced-started.md +++ b/docs/getting-started/advanced-started.md @@ -93,7 +93,7 @@ meters in distance and elevation gain. Some notes: ## Importing information from a Strava bulk export (BETA) -Strava allows uesrs to create a bulk export of their historical activity on the site. This information is stored in a zip file, primarily as .csv files, GPS recording files (e.g., .gpx, .fit), and media files (e.g., .jpg, .png). +Strava allows users to create a bulk export of their historical activity on the site. This information is stored in a zip file, primarily as .csv files, GPS recording files (e.g., .gpx, .fit), and media files (e.g., .jpg, .png). At the present time, importing bikes from a Strava bulk export is implemented as a beta feature - use with caution. Components of bikes are not imported - just the bikes themselves. From 0cb892723e708bf56c9a56fcabcc45c87860d7f3 Mon Sep 17 00:00:00 2001 From: "Marc @PDev1 server" Date: Thu, 31 Jul 2025 06:41:13 +0000 Subject: [PATCH 21/92] Revert "Ignore this add - ignoring personal development scripts." This reverts commit 04b36020ddc649e3331229c5203411e625348476. These are just personal scripts - this ignore should not have been pushed out. --- .gitignore | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 0ddfb4e5c..c22fc1b97 100644 --- a/.gitignore +++ b/.gitignore @@ -90,9 +90,4 @@ memory-bank/ .clinerules # local postgres -postgres/ - -# Perkins scripts -cycle-docker.sh -build-new-image.sh - +postgres/ \ No newline at end of file From 1a5681db608afc1a6a3e7ec170ed1b1c1062eb09 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Sep 2025 19:59:22 +0000 Subject: [PATCH 22/92] Initial plan From 72a0af4f8dccd4964009fea0bd662ef8a20ba423 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Sep 2025 20:11:34 +0000 Subject: [PATCH 23/92] Implement backend signup infrastructure and database models Co-authored-by: joaovitoriasilva <8648976+joaovitoriasilva@users.noreply.github.com> --- .../versions/v0_15_0_signup_migration.py | 42 +++ backend/app/server_settings/models.py | 18 + backend/app/server_settings/schema.py | 3 + backend/app/session/router.py | 95 ++++++ backend/app/users/user/crud.py | 116 +++++++ backend/app/users/user/models.py | 17 + backend/app/users/user/schema.py | 19 ++ backend/app/users/user/utils.py | 15 + frontend/app/src/router/index.js | 12 + frontend/app/src/services/sessionService.js | 6 + .../app/src/views/EmailVerificationView.vue | 91 +++++ frontend/app/src/views/LoginView.vue | 16 + frontend/app/src/views/SignUpView.vue | 315 ++++++++++++++++++ 13 files changed, 765 insertions(+) create mode 100644 backend/app/alembic/versions/v0_15_0_signup_migration.py create mode 100644 frontend/app/src/views/EmailVerificationView.vue create mode 100644 frontend/app/src/views/SignUpView.vue diff --git a/backend/app/alembic/versions/v0_15_0_signup_migration.py b/backend/app/alembic/versions/v0_15_0_signup_migration.py new file mode 100644 index 000000000..59c3cbeec --- /dev/null +++ b/backend/app/alembic/versions/v0_15_0_signup_migration.py @@ -0,0 +1,42 @@ +"""v0.15.0 signup support migration + +Revision ID: 3c4d5e6f7a8b +Revises: 86b2e24e227e +Create Date: 2025-01-01 12:00:00.000000 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision: str = "3c4d5e6f7a8b" +down_revision: Union[str, None] = "86b2e24e227e" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Add new columns to server_settings table + op.add_column('server_settings', sa.Column('signup_enabled', sa.Boolean(), nullable=False, default=False, comment='Allow user sign-up registration (true - yes, false - no)')) + op.add_column('server_settings', sa.Column('signup_require_admin_approval', sa.Boolean(), nullable=False, default=True, comment='Require admin approval for new sign-ups (true - yes, false - no)')) + op.add_column('server_settings', sa.Column('signup_require_email_verification', sa.Boolean(), nullable=False, default=True, comment='Require email verification for new sign-ups (true - yes, false - no)')) + + # Add new columns to users table + op.add_column('users', sa.Column('email_verified', sa.Boolean(), nullable=False, default=False, comment='Whether the user\'s email address has been verified')) + op.add_column('users', sa.Column('email_verification_token', sa.String(length=255), nullable=True, comment='Token for email verification')) + op.add_column('users', sa.Column('pending_admin_approval', sa.Boolean(), nullable=False, default=False, comment='Whether the user is pending admin approval for activation')) + + +def downgrade() -> None: + # Remove columns from users table + op.drop_column('users', 'pending_admin_approval') + op.drop_column('users', 'email_verification_token') + op.drop_column('users', 'email_verified') + + # Remove columns from server_settings table + op.drop_column('server_settings', 'signup_require_email_verification') + op.drop_column('server_settings', 'signup_require_admin_approval') + op.drop_column('server_settings', 'signup_enabled') \ No newline at end of file diff --git a/backend/app/server_settings/models.py b/backend/app/server_settings/models.py index 3a98787dc..0879379f5 100644 --- a/backend/app/server_settings/models.py +++ b/backend/app/server_settings/models.py @@ -42,5 +42,23 @@ class ServerSettings(Base): default=25, comment="Number of records per page in lists", ) + signup_enabled = Column( + Boolean, + nullable=False, + default=False, + comment="Allow user sign-up registration (true - yes, false - no)", + ) + signup_require_admin_approval = Column( + Boolean, + nullable=False, + default=True, + comment="Require admin approval for new sign-ups (true - yes, false - no)", + ) + signup_require_email_verification = Column( + Boolean, + nullable=False, + default=True, + comment="Require email verification for new sign-ups (true - yes, false - no)", + ) __table_args__ = (CheckConstraint("id = 1", name="single_row_check"),) diff --git a/backend/app/server_settings/schema.py b/backend/app/server_settings/schema.py index d50ec6735..e17d54e05 100644 --- a/backend/app/server_settings/schema.py +++ b/backend/app/server_settings/schema.py @@ -9,6 +9,9 @@ class ServerSettings(BaseModel): login_photo_set: bool currency: int num_records_per_page: int + signup_enabled: bool + signup_require_admin_approval: bool + signup_require_email_verification: bool model_config = { "from_attributes": True diff --git a/backend/app/session/router.py b/backend/app/session/router.py index 7ac34e034..896530ea3 100644 --- a/backend/app/session/router.py +++ b/backend/app/session/router.py @@ -16,10 +16,18 @@ import session.utils as session_utils import session.security as session_security import session.crud as session_crud import session.schema as session_schema +import session.constants as session_constants import users.user.crud as users_crud import users.user.utils as users_utils +import users.user.schema as users_schema +import users.user_integrations.crud as user_integrations_crud +import users.user_default_gear.crud as user_default_gear_crud +import users.user_privacy_settings.crud as users_privacy_settings_crud + +import health_targets.crud as health_targets_crud import profile.utils as profile_utils +import server_settings.crud as server_settings_crud import core.database as core_database @@ -103,6 +111,93 @@ async def complete_login(response: Response, request: Request, user, client_type ) +@router.post("/signup", status_code=201) +async def signup( + user: users_schema.UserSignup, + db: Annotated[ + Session, + Depends(core_database.get_db), + ], +): + """Public endpoint for user sign-up""" + # Get server settings to check if signup is enabled + server_settings = server_settings_crud.get_server_settings(db) + if not server_settings: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Server settings not configured", + ) + + # Check if signup is enabled + users_utils.check_user_can_signup(server_settings) + + # Generate email verification token if needed + email_verification_token = None + if server_settings.signup_require_email_verification: + email_verification_token = users_utils.generate_email_verification_token() + + # Create the user in the database + created_user = users_crud.create_signup_user(user, email_verification_token, server_settings, db) + + # Create the user integrations in the database + user_integrations_crud.create_user_integrations(created_user.id, db) + + # Create the user privacy settings + users_privacy_settings_crud.create_user_privacy_settings(created_user.id, db) + + # Create the user health targets + health_targets_crud.create_health_targets(created_user.id, db) + + # Create the user default gear + user_default_gear_crud.create_user_default_gear(created_user.id, db) + + # Return appropriate response based on server configuration + response_data = {"message": "User created successfully"} + + if server_settings.signup_require_email_verification: + response_data["message"] = "User created successfully. Please check your email for verification instructions." + response_data["email_verification_required"] = True + # TODO: Send verification email here + + if server_settings.signup_require_admin_approval: + response_data["message"] = "User created successfully. Account is pending admin approval." + response_data["admin_approval_required"] = True + + if not server_settings.signup_require_email_verification and not server_settings.signup_require_admin_approval: + response_data["message"] = "User created successfully. You can now log in." + + return response_data + + +@router.get("/verify-email/{token}") +async def verify_email( + token: str, + db: Annotated[ + Session, + Depends(core_database.get_db), + ], +): + """Public endpoint for email verification""" + # Get server settings + server_settings = server_settings_crud.get_server_settings(db) + if not server_settings or not server_settings.signup_require_email_verification: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Email verification is not enabled", + ) + + # Verify the email + user = users_crud.verify_user_email(token, db) + + message = "Email verified successfully." + if user.pending_admin_approval: + message += " Your account is now pending admin approval." + else: + message += " You can now log in." + + return {"message": message, "user_active": user.is_active == session_constants.USER_ACTIVE} + + @router.post("/mfa/verify") async def verify_mfa_and_login( response: Response, diff --git a/backend/app/users/user/crud.py b/backend/app/users/user/crud.py index f2d112dc3..1690b82eb 100644 --- a/backend/app/users/user/crud.py +++ b/backend/app/users/user/crud.py @@ -580,3 +580,119 @@ def disable_user_mfa(user_id: int, db: Session): status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal Server Error", ) from err + + +def create_signup_user(user: users_schema.UserSignup, email_verification_token: str, server_settings, db: Session): + """Create a new user via sign-up with appropriate verification and approval settings""" + try: + # Determine user status based on server settings + is_active = session_constants.USER_ACTIVE + email_verified = False + pending_admin_approval = False + + if server_settings.signup_require_email_verification: + email_verified = False + is_active = session_constants.USER_NOT_ACTIVE # Inactive until email verified + + if server_settings.signup_require_admin_approval: + pending_admin_approval = True + is_active = session_constants.USER_NOT_ACTIVE # Inactive until approved + + # If both email verification and admin approval are disabled, user is immediately active + if not server_settings.signup_require_email_verification and not server_settings.signup_require_admin_approval: + is_active = session_constants.USER_ACTIVE + email_verified = True + + # Create a new user + db_user = users_models.User( + name=user.name, + username=user.username, + email=user.email, + city=user.city, + birthdate=user.birthdate, + preferred_language=user.preferred_language, + gender=user.gender, + units=user.units, + height=user.height, + access_type=session_constants.REGULAR_ACCESS, + is_active=is_active, + first_day_of_week=user.first_day_of_week, + currency=user.currency, + email_verified=email_verified, + email_verification_token=email_verification_token if server_settings.signup_require_email_verification else None, + pending_admin_approval=pending_admin_approval, + password=session_security.hash_password(user.password), + ) + + # Add the user to the database + db.add(db_user) + db.commit() + db.refresh(db_user) + + # Return user + return db_user + except IntegrityError as integrity_error: + # Rollback the transaction + db.rollback() + + # Raise an HTTPException with a 409 Conflict status code + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Duplicate entry error. Check if email and username are unique", + ) from integrity_error + except Exception as err: + # Rollback the transaction + db.rollback() + + # Log the exception + core_logger.print_to_log(f"Error in create_signup_user: {err}", "error", exc=err) + + # Raise an HTTPException with a 500 Internal Server Error status code + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Internal Server Error", + ) from err + + +def verify_user_email(token: str, db: Session): + """Verify user email using the verification token""" + try: + # Find user by verification token + db_user = ( + db.query(users_models.User) + .filter(users_models.User.email_verification_token == token) + .first() + ) + + if db_user is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Invalid or expired verification token", + ) + + # Mark email as verified and remove token + db_user.email_verified = True + db_user.email_verification_token = None + + # If not pending admin approval, activate the user + if not db_user.pending_admin_approval: + db_user.is_active = session_constants.USER_ACTIVE + + db.commit() + db.refresh(db_user) + + return db_user + except HTTPException as http_err: + raise http_err + except Exception as err: + # Rollback the transaction + db.rollback() + + # Log the exception + core_logger.print_to_log(f"Error in verify_user_email: {err}", "error", exc=err) + + # Raise an HTTPException with a 500 Internal Server Error status code + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Internal Server Error", + ) from err diff --git a/backend/app/users/user/models.py b/backend/app/users/user/models.py index c7b0430c5..022fe85e8 100644 --- a/backend/app/users/user/models.py +++ b/backend/app/users/user/models.py @@ -88,6 +88,23 @@ class User(Base): nullable=True, comment="User MFA secret for TOTP generation (encrypted at rest)", ) + email_verified = Column( + Boolean, + nullable=False, + default=False, + comment="Whether the user's email address has been verified", + ) + email_verification_token = Column( + String(length=255), + nullable=True, + comment="Token for email verification", + ) + pending_admin_approval = Column( + Boolean, + nullable=False, + default=False, + comment="Whether the user is pending admin approval for activation", + ) # Define a relationship to UsersSessions model users_sessions = relationship( diff --git a/backend/app/users/user/schema.py b/backend/app/users/user/schema.py index e4f79b942..37e611aa9 100644 --- a/backend/app/users/user/schema.py +++ b/backend/app/users/user/schema.py @@ -66,3 +66,22 @@ class UserEditPassword(BaseModel): @field_validator("password") def validate_password_field(cls, value): return validate_password(value) + + +class UserSignup(BaseModel): + name: str + username: str + email: EmailStr + city: str | None = None + birthdate: str | None = None + preferred_language: str = "en" + gender: int = 1 # Default to male + units: int = 1 # Default to metric + height: int | None = None + first_day_of_week: int = 1 + currency: int = 1 # Default to euro + password: str + + @field_validator("password") + def validate_password_field(cls, value): + return validate_password(value) diff --git a/backend/app/users/user/utils.py b/backend/app/users/user/utils.py index 0e4d2d247..52b701e47 100644 --- a/backend/app/users/user/utils.py +++ b/backend/app/users/user/utils.py @@ -1,5 +1,6 @@ import os import glob +import secrets from fastapi import HTTPException, status, UploadFile from sqlalchemy.orm import Session @@ -72,3 +73,17 @@ async def save_user_image(user_id: int, file: UploadFile, db: Session): status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal Server Error", ) from err + + +def generate_email_verification_token(): + """Generate a secure token for email verification""" + return secrets.token_urlsafe(32) + + +def check_user_can_signup(server_settings) -> None: + """Check if user signup is enabled on the server""" + if not server_settings.signup_enabled: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="User sign-up is not enabled on this server", + ) diff --git a/frontend/app/src/router/index.js b/frontend/app/src/router/index.js index 6af6c2f36..765bdcf8e 100644 --- a/frontend/app/src/router/index.js +++ b/frontend/app/src/router/index.js @@ -15,6 +15,18 @@ const routes = [ component: () => import('../views/LoginView.vue'), meta: { requiresAuth: false } }, + { + path: '/signup', + name: 'signup', + component: () => import('../views/SignUpView.vue'), + meta: { requiresAuth: false } + }, + { + path: '/verify-email/:token', + name: 'verify-email', + component: () => import('../views/EmailVerificationView.vue'), + meta: { requiresAuth: false } + }, { path: '/reset-password', name: 'reset-password', diff --git a/frontend/app/src/services/sessionService.js b/frontend/app/src/services/sessionService.js index 994e26077..758d84e11 100644 --- a/frontend/app/src/services/sessionService.js +++ b/frontend/app/src/services/sessionService.js @@ -20,5 +20,11 @@ export const session = { }, logoutUser() { return fetchPostRequest('logout', null) + }, + signUp(userData) { + return fetchPostRequest('signup', userData) + }, + verifyEmail(token) { + return fetchGetRequest(`verify-email/${token}`) } } diff --git a/frontend/app/src/views/EmailVerificationView.vue b/frontend/app/src/views/EmailVerificationView.vue new file mode 100644 index 000000000..490b580a7 --- /dev/null +++ b/frontend/app/src/views/EmailVerificationView.vue @@ -0,0 +1,91 @@ + + + \ No newline at end of file diff --git a/frontend/app/src/views/LoginView.vue b/frontend/app/src/views/LoginView.vue index 7c55d4df0..4daca3d40 100644 --- a/frontend/app/src/views/LoginView.vue +++ b/frontend/app/src/views/LoginView.vue @@ -91,6 +91,14 @@ {{ $t('loginView.forgotPassword') }}{{ $t('generalItems.betaTag') }} +
    + + {{ $t('loginView.signUpLink') }} + +
    +
    + + +
    {{ errors.name }}
    +
    + + +
    + + +
    {{ errors.username }}
    +
    + + +
    + + +
    {{ errors.email }}
    +
    + + +
    + + + +
    {{ errors.password }}
    +
    + + +
    + +
    + +
    + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    +
    + + + +
    + + {{ $t('signupView.alreadyHaveAccount') }} + +
    + + + + + + + \ No newline at end of file From 12f4292d03d31871d9f87b92d5c6673792b71122 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Sep 2025 20:17:20 +0000 Subject: [PATCH 24/92] Complete sign-up feature with localization and final testing Co-authored-by: joaovitoriasilva <8648976+joaovitoriasilva@users.noreply.github.com> --- backend/app/users/user/schema.py | 3 ++ frontend/app/src/i18n/index.js | 4 ++- .../app/src/i18n/us/emailVerification.json | 14 +++++++++ frontend/app/src/i18n/us/loginView.json | 5 +++- frontend/app/src/i18n/us/signupView.json | 30 +++++++++++++++++++ 5 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 frontend/app/src/i18n/us/emailVerification.json create mode 100644 frontend/app/src/i18n/us/signupView.json diff --git a/backend/app/users/user/schema.py b/backend/app/users/user/schema.py index 37e611aa9..24398bcc7 100644 --- a/backend/app/users/user/schema.py +++ b/backend/app/users/user/schema.py @@ -30,6 +30,9 @@ class User(BaseModel): currency: int mfa_enabled: bool = False mfa_secret: str | None = None + email_verified: bool = False + email_verification_token: str | None = None + pending_admin_approval: bool = False model_config = {"from_attributes": True} diff --git a/frontend/app/src/i18n/index.js b/frontend/app/src/i18n/index.js index 489a8b0a0..bb89c4e4f 100644 --- a/frontend/app/src/i18n/index.js +++ b/frontend/app/src/i18n/index.js @@ -98,7 +98,9 @@ const componentPaths = { settingsView: 'settingsView.json', userView: 'userView.json', summaryView: 'summaryView.json', - resetPassword: 'resetPassword.json' + resetPassword: 'resetPassword.json', + signupView: 'signupView.json', + emailVerification: 'emailVerification.json' } // Reverse map: relative path -> semantic key diff --git a/frontend/app/src/i18n/us/emailVerification.json b/frontend/app/src/i18n/us/emailVerification.json new file mode 100644 index 000000000..8317204c6 --- /dev/null +++ b/frontend/app/src/i18n/us/emailVerification.json @@ -0,0 +1,14 @@ +{ + "verifying": "Verifying your email...", + "pleaseWait": "Please wait while we verify your email address", + "success": "Email Verified!", + "error": "Verification Failed", + "goToLogin": "Go to Login", + "backToLogin": "Back to Login", + "invalidToken": "Invalid or missing verification token", + "verificationSuccess": "Your email has been successfully verified", + "tokenNotFound": "Invalid or expired verification token", + "tokenExpired": "Verification token has expired", + "verificationFailed": "Email verification failed. Please try again", + "emailVerified": "Email verified successfully" +} \ No newline at end of file diff --git a/frontend/app/src/i18n/us/loginView.json b/frontend/app/src/i18n/us/loginView.json index 6729d39ac..1c49d3247 100644 --- a/frontend/app/src/i18n/us/loginView.json +++ b/frontend/app/src/i18n/us/loginView.json @@ -29,5 +29,8 @@ "forgotPasswordModalRequestSuccess": "If the email exists in the system, a password reset link will be sent to the provided email address", "forgotPasswordModalRequestError": "Failed to process password reset request", "forgotPasswordModalEmailNotConfigured": "Email service is not configured. Please contact the administrator", - "forgotPasswordModalUnableToSendEmail": "Unable to send email. Please try again later or contact the administrator" + "forgotPasswordModalUnableToSendEmail": "Unable to send email. Please try again later or contact the administrator", + "signUpLink": "Don't have an account? Sign up", + "emailVerificationSent": "Please check your email for verification instructions", + "adminApprovalRequired": "Your account is pending admin approval" } diff --git a/frontend/app/src/i18n/us/signupView.json b/frontend/app/src/i18n/us/signupView.json new file mode 100644 index 000000000..57a6db4b1 --- /dev/null +++ b/frontend/app/src/i18n/us/signupView.json @@ -0,0 +1,30 @@ +{ + "subtitle": "Create your account below", + "name": "Full Name", + "username": "Username", + "email": "Email Address", + "password": "Password", + "city": "City", + "birthdate": "Birth Date", + "gender": "Gender", + "male": "Male", + "female": "Female", + "unspecified": "Prefer not to say", + "units": "Units", + "metric": "Metric", + "imperial": "Imperial", + "height": "Height (cm)", + "signUpButton": "Create Account", + "alreadyHaveAccount": "Already have an account? Sign in", + "errorNameRequired": "Full name is required", + "errorUsernameRequired": "Username is required", + "errorEmailRequired": "Email address is required", + "errorEmailInvalid": "Please enter a valid email address", + "errorPasswordRequired": "Password is required", + "errorPasswordTooShort": "Password must be at least 8 characters long", + "errorUserExists": "A user with this email or username already exists", + "errorSignupDisabled": "Sign-up is not enabled on this server", + "errorValidation": "Please check your input and try again", + "errorGeneral": "An error occurred during sign-up", + "signupDisabled": "User sign-up is not enabled on this server" +} \ No newline at end of file From 05b9403a00e13423913fe71864486c3e723f9da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vit=C3=B3ria=20Silva?= <8648976+joaovitoriasilva@users.noreply.github.com> Date: Sat, 6 Sep 2025 18:35:58 +0100 Subject: [PATCH 25/92] Refactor signup migration and update user model comments Replaces the previous signup migration with a new, more robust Alembic migration for v0.15.0, adding signup and verification columns to server_settings and users tables. Also updates comments in the User model for clarity regarding boolean fields. --- .../app/alembic/versions/v0_15_0_migration.py | 164 ++++++++++++++++++ .../versions/v0_15_0_signup_migration.py | 42 ----- backend/app/users/user/models.py | 12 +- 3 files changed, 167 insertions(+), 51 deletions(-) create mode 100644 backend/app/alembic/versions/v0_15_0_migration.py delete mode 100644 backend/app/alembic/versions/v0_15_0_signup_migration.py diff --git a/backend/app/alembic/versions/v0_15_0_migration.py b/backend/app/alembic/versions/v0_15_0_migration.py new file mode 100644 index 000000000..27ecd1e79 --- /dev/null +++ b/backend/app/alembic/versions/v0_15_0_migration.py @@ -0,0 +1,164 @@ +"""v0.15.0 migration + +Revision ID: 3c4d5e6f7a8b +Revises: 86b2e24e227e +Create Date: 2025-01-01 12:00:00.000000 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision: str = "3c4d5e6f7a8b" +down_revision: Union[str, None] = "86b2e24e227e" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Add new columns to server_settings table + op.add_column( + "server_settings", + sa.Column( + "signup_enabled", + sa.Boolean(), + nullable=True, + default=False, + comment="Allow user sign-up registration (true - yes, false - no)", + ), + ) + op.execute( + """ + UPDATE server_settings + SET signup_enabled = false + WHERE signup_enabled IS NULL; + """ + ) + op.alter_column( + "server_settings", + "signup_enabled", + nullable=False, + comment="Allow user sign-up registration (true - yes, false - no)", + existing_type=sa.Boolean(), + ) + op.add_column( + "server_settings", + sa.Column( + "signup_require_admin_approval", + sa.Boolean(), + nullable=True, + default=True, + comment="Require admin approval for new sign-ups (true - yes, false - no)", + ), + ) + op.execute( + """ + UPDATE server_settings + SET signup_require_admin_approval = false + WHERE signup_require_admin_approval IS NULL; + """ + ) + op.alter_column( + "server_settings", + "signup_require_admin_approval", + nullable=False, + comment="Require admin approval for new sign-ups (true - yes, false - no)", + existing_type=sa.Boolean(), + ) + op.add_column( + "server_settings", + sa.Column( + "signup_require_email_verification", + sa.Boolean(), + nullable=True, + default=True, + comment="Require email verification for new sign-ups (true - yes, false - no)", + ), + ) + op.execute( + """ + UPDATE server_settings + SET signup_require_email_verification = false + WHERE signup_require_email_verification IS NULL; + """ + ) + op.alter_column( + "server_settings", + "signup_require_email_verification", + nullable=False, + comment="Require email verification for new sign-ups (true - yes, false - no)", + existing_type=sa.Boolean(), + ) + # Add new columns to users table + op.add_column( + "users", + sa.Column( + "email_verified", + sa.Boolean(), + nullable=True, + default=False, + comment="Whether the user's email address has been verified (true - yes, false - no)", + ), + ) + op.execute( + """ + UPDATE users + SET email_verified = true + WHERE email_verified IS NULL; + """ + ) + op.alter_column( + "users", + "email_verified", + nullable=False, + comment="Whether the user's email address has been verified (true - yes, false - no)", + existing_type=sa.Boolean(), + ) + op.add_column( + "users", + sa.Column( + "email_verification_token", + sa.String(length=255), + nullable=True, + comment="Token for email verification", + ), + ) + op.add_column( + "users", + sa.Column( + "pending_admin_approval", + sa.Boolean(), + nullable=True, + default=False, + comment="Whether the user is pending admin approval for activation (true - yes, false - no)", + ), + ) + op.execute( + """ + UPDATE users + SET pending_admin_approval = false + WHERE pending_admin_approval IS NULL; + """ + ) + op.alter_column( + "users", + "pending_admin_approval", + nullable=False, + comment="Whether the user is pending admin approval for activation (true - yes, false - no)", + existing_type=sa.Boolean(), + ) + + +def downgrade() -> None: + # Remove columns from users table + op.drop_column("users", "pending_admin_approval") + op.drop_column("users", "email_verification_token") + op.drop_column("users", "email_verified") + + # Remove columns from server_settings table + op.drop_column("server_settings", "signup_require_email_verification") + op.drop_column("server_settings", "signup_require_admin_approval") + op.drop_column("server_settings", "signup_enabled") diff --git a/backend/app/alembic/versions/v0_15_0_signup_migration.py b/backend/app/alembic/versions/v0_15_0_signup_migration.py deleted file mode 100644 index 59c3cbeec..000000000 --- a/backend/app/alembic/versions/v0_15_0_signup_migration.py +++ /dev/null @@ -1,42 +0,0 @@ -"""v0.15.0 signup support migration - -Revision ID: 3c4d5e6f7a8b -Revises: 86b2e24e227e -Create Date: 2025-01-01 12:00:00.000000 - -""" - -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision: str = "3c4d5e6f7a8b" -down_revision: Union[str, None] = "86b2e24e227e" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - # Add new columns to server_settings table - op.add_column('server_settings', sa.Column('signup_enabled', sa.Boolean(), nullable=False, default=False, comment='Allow user sign-up registration (true - yes, false - no)')) - op.add_column('server_settings', sa.Column('signup_require_admin_approval', sa.Boolean(), nullable=False, default=True, comment='Require admin approval for new sign-ups (true - yes, false - no)')) - op.add_column('server_settings', sa.Column('signup_require_email_verification', sa.Boolean(), nullable=False, default=True, comment='Require email verification for new sign-ups (true - yes, false - no)')) - - # Add new columns to users table - op.add_column('users', sa.Column('email_verified', sa.Boolean(), nullable=False, default=False, comment='Whether the user\'s email address has been verified')) - op.add_column('users', sa.Column('email_verification_token', sa.String(length=255), nullable=True, comment='Token for email verification')) - op.add_column('users', sa.Column('pending_admin_approval', sa.Boolean(), nullable=False, default=False, comment='Whether the user is pending admin approval for activation')) - - -def downgrade() -> None: - # Remove columns from users table - op.drop_column('users', 'pending_admin_approval') - op.drop_column('users', 'email_verification_token') - op.drop_column('users', 'email_verified') - - # Remove columns from server_settings table - op.drop_column('server_settings', 'signup_require_email_verification') - op.drop_column('server_settings', 'signup_require_admin_approval') - op.drop_column('server_settings', 'signup_enabled') \ No newline at end of file diff --git a/backend/app/users/user/models.py b/backend/app/users/user/models.py index 022fe85e8..c11120fef 100644 --- a/backend/app/users/user/models.py +++ b/backend/app/users/user/models.py @@ -1,10 +1,4 @@ -from sqlalchemy import ( - Column, - Integer, - String, - Date, - Boolean -) +from sqlalchemy import Column, Integer, String, Date, Boolean from sqlalchemy.orm import relationship from core.database import Base @@ -92,7 +86,7 @@ class User(Base): Boolean, nullable=False, default=False, - comment="Whether the user's email address has been verified", + comment="Whether the user's email address has been verified (true - yes, false - no)", ) email_verification_token = Column( String(length=255), @@ -103,7 +97,7 @@ class User(Base): Boolean, nullable=False, default=False, - comment="Whether the user is pending admin approval for activation", + comment="Whether the user is pending admin approval for activation (true - yes, false - no)", ) # Define a relationship to UsersSessions model From 4543a14758c48f47e6444e64fab3eb829f4c29cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vit=C3=B3ria=20Silva?= <8648976+joaovitoriasilva@users.noreply.github.com> Date: Sat, 6 Sep 2025 18:46:00 +0100 Subject: [PATCH 26/92] Refactor ServerSettings schema with enums and stricter types Replaced integer fields for units and currency with IntEnum-based types for better type safety and clarity. Added detailed docstrings and stricter validation using StrictInt and Pydantic's ConfigDict. Enhanced model configuration to forbid extra fields and validate assignments. --- backend/app/server_settings/schema.py | 64 ++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/backend/app/server_settings/schema.py b/backend/app/server_settings/schema.py index e17d54e05..4a755be7d 100644 --- a/backend/app/server_settings/schema.py +++ b/backend/app/server_settings/schema.py @@ -1,18 +1,68 @@ -from pydantic import BaseModel +from enum import IntEnum +from pydantic import BaseModel, StrictInt, ConfigDict + + +class Units(IntEnum): + """ + An enumeration representing measurement units. + + Attributes: + METRIC (int): Metric system (e.g., meters, kilograms). + IMPERIAL (int): Imperial system (e.g., miles, pounds). + """ + + METRIC = 1 + IMPERIAL = 2 + + +class Currency(IntEnum): + """ + An enumeration representing supported currencies. + + Attributes: + EURO (int): Represents the Euro currency. + DOLLAR (int): Represents the US Dollar currency. + POUND (int): Represents the British Pound currency. + """ + + EURO = 1 + DOLLAR = 2 + POUND = 3 class ServerSettings(BaseModel): - id: int - units: int + """ + Represents the configuration settings for the server. + + Attributes: + id (StrictInt): Unique identifier for the server settings. + units (Units): Measurement units used by the server. + public_shareable_links (bool): Indicates if public shareable links are enabled. + public_shareable_links_user_info (bool): Indicates if user info is included in public shareable links. + login_photo_set (bool): Indicates if a login photo is set. + currency (Currency): Currency used by the server. + num_records_per_page (int): Number of records displayed per page. + signup_enabled (bool): Indicates if user signup is enabled. + signup_require_admin_approval (bool): Indicates if admin approval is required for signup. + signup_require_email_verification (bool): Indicates if email verification is required for signup. + + Config: + from_attributes (bool): Allows model creation from attribute dictionaries. + extra (str): Forbids extra fields not defined in the model. + validate_assignment (bool): Enables validation on assignment. + """ + + id: StrictInt + units: Units public_shareable_links: bool public_shareable_links_user_info: bool login_photo_set: bool - currency: int + currency: Currency num_records_per_page: int signup_enabled: bool signup_require_admin_approval: bool signup_require_email_verification: bool - model_config = { - "from_attributes": True - } \ No newline at end of file + model_config = ConfigDict( + from_attributes=True, extra="forbid", validate_assignment=True + ) From a0e9342894b30f39a3e41f6d72c9bc80eebc7b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vit=C3=B3ria=20Silva?= <8648976+joaovitoriasilva@users.noreply.github.com> Date: Sat, 6 Sep 2025 19:49:28 +0100 Subject: [PATCH 27/92] Refactor is_active to active boolean for users, gear, and components Replaces the 'is_active' integer field with a boolean 'active' field in users, gear, and gear_components models, schemas, and related backend/frontend logic. Updates migration scripts, API, and UI components to use the new field, improving consistency and clarity for active status handling. --- .../app/alembic/versions/v0_15_0_migration.py | 172 ++++++++++++++++++ backend/app/garmin/gear_utils.py | 2 +- backend/app/gears/gear/crud.py | 4 +- backend/app/gears/gear/models.py | 5 +- backend/app/gears/gear/schema.py | 2 +- backend/app/gears/gear/utils.py | 2 +- backend/app/gears/gear_components/crud.py | 2 +- backend/app/gears/gear_components/models.py | 7 +- backend/app/gears/gear_components/schema.py | 4 +- backend/app/password_reset_tokens/utils.py | 4 +- backend/app/session/router.py | 71 +++++--- backend/app/session/security.py | 2 +- backend/app/strava/gear_utils.py | 2 +- backend/app/users/user/crud.py | 18 +- backend/app/users/user/models.py | 7 +- backend/app/users/user/schema.py | 105 +++++++---- backend/app/users/user/utils.py | 2 +- .../GearComponentAddEditModalComponent.vue | 12 +- .../Gears/GearComponentListComponent.vue | 2 +- .../Gears/GearsAddEditGearModalComponent.vue | 22 +-- .../components/Gears/GearsListComponent.vue | 2 +- .../UsersAddEditUserModalComponent.vue | 16 +- .../SettingsUsersZone/UsersListComponent.vue | 2 +- frontend/app/src/stores/authStore.js | 4 +- frontend/app/src/views/Gears/GearView.vue | 4 +- frontend/app/src/views/SearchView.vue | 2 +- 26 files changed, 348 insertions(+), 129 deletions(-) diff --git a/backend/app/alembic/versions/v0_15_0_migration.py b/backend/app/alembic/versions/v0_15_0_migration.py index 27ecd1e79..0a0cc1bde 100644 --- a/backend/app/alembic/versions/v0_15_0_migration.py +++ b/backend/app/alembic/versions/v0_15_0_migration.py @@ -150,10 +150,182 @@ def upgrade() -> None: comment="Whether the user is pending admin approval for activation (true - yes, false - no)", existing_type=sa.Boolean(), ) + op.add_column( + "users", + sa.Column( + "active", + sa.Boolean(), + nullable=True, + default=False, + comment="Whether the user is active (true - yes, false - no)", + ), + ) + op.execute( + """ + UPDATE users + SET active = true + WHERE is_active = 1; + UPDATE users + SET active = false + WHERE is_active = 2; + """ + ) + op.alter_column( + "users", + "active", + nullable=False, + comment="Whether the user is active (true - yes, false - no)", + existing_type=sa.Boolean(), + ) + op.drop_column("users", "is_active") + # Add new columns to gear table + op.add_column( + "gear", + sa.Column( + "active", + sa.Boolean(), + nullable=True, + default=True, + comment="Whether the gear is active (true - yes, false - no)", + ), + ) + op.execute( + """ + UPDATE gear + SET active = true + WHERE is_active = 1; + UPDATE gear + SET active = false + WHERE is_active = 0; + """ + ) + op.alter_column( + "gear", + "active", + nullable=False, + comment="Whether the gear is active (true - yes, false - no)", + existing_type=sa.Boolean(), + ) + op.drop_column("gear", "is_active") + # Add new columns to gear component table + op.add_column( + "gear_components", + sa.Column( + "active", + sa.Boolean(), + nullable=True, + default=True, + comment="Whether the gear component is active (true - yes, false - no)", + ), + ) + op.execute( + """ + UPDATE gear_components + SET active = true + WHERE is_active = true; + UPDATE gear_components + SET active = false + WHERE is_active = false; + """ + ) + op.alter_column( + "gear_components", + "active", + nullable=False, + comment="Whether the gear component is active (true - yes, false - no)", + existing_type=sa.Boolean(), + ) + op.drop_column("gear_components", "is_active") def downgrade() -> None: + # Remove columns from gear_components table + op.add_column( + "gear_components", + sa.Column( + "is_active", + sa.Boolean(), + nullable=True, + default=True, + comment="Is gear component active", + ), + ) + op.execute( + """ + UPDATE gear_components + SET is_active = true + WHERE active = true; + UPDATE gear_components + SET is_active = false + WHERE active = false; + """ + ) + op.alter_column( + "gear_components", + "is_active", + nullable=False, + comment="Is gear component active", + existing_type=sa.Boolean(), + ) + op.drop_column("gear_components", "active") + # Remove columns from gear table + op.add_column( + "gear", + sa.Column( + "is_active", + sa.Integer(), + nullable=True, + default=1, + comment="Is gear active (0 - not active, 1 - active)", + ), + ) + op.execute( + """ + UPDATE gear + SET is_active = 1 + WHERE active = true; + UPDATE gear + SET is_active = 0 + WHERE active = false; + """ + ) + op.alter_column( + "gear", + "is_active", + nullable=False, + comment="Is gear active (0 - not active, 1 - active)", + existing_type=sa.Integer(), + ) + op.drop_column("gear", "active") # Remove columns from users table + op.add_column( + "users", + sa.Column( + "is_active", + sa.Integer(), + nullable=True, + default=1, + comment="Is user active (1 - active, 2 - not active)", + ), + ) + op.execute( + """ + UPDATE users + SET is_active = 1 + WHERE active = true; + UPDATE users + SET is_active = 2 + WHERE active = false; + """ + ) + op.alter_column( + "users", + "is_active", + nullable=False, + comment="Is user active (1 - active, 2 - not active)", + existing_type=sa.Integer(), + ) + op.drop_column("users", "active") op.drop_column("users", "pending_admin_approval") op.drop_column("users", "email_verification_token") op.drop_column("users", "email_verified") diff --git a/backend/app/garmin/gear_utils.py b/backend/app/garmin/gear_utils.py index 58e00647d..90c9590a7 100644 --- a/backend/app/garmin/gear_utils.py +++ b/backend/app/garmin/gear_utils.py @@ -63,7 +63,7 @@ def process_gear(gear, user_id: int, db: Session) -> gears_schema.Gear | None: nickname=gear["displayName"] if gear["displayName"] else gear["customMakeModel"], gear_type=1 if gear["gearTypeName"] == "Bike" else 2, user_id=user_id, - is_active=1 if gear["gearStatusName"] == "active" else 0, + active=True if gear["gearStatusName"] == "active" else False, garminconnect_gear_id=gear["uuid"], ) diff --git a/backend/app/gears/gear/crud.py b/backend/app/gears/gear/crud.py index ff9901f8e..280714371 100644 --- a/backend/app/gears/gear/crud.py +++ b/backend/app/gears/gear/crud.py @@ -403,8 +403,8 @@ def edit_gear(gear_id: int, gear: gears_schema.Gear, db: Session): db_gear.gear_type = gear.gear_type if gear.created_at is not None: db_gear.created_at = gear.created_at - if gear.is_active is not None: - db_gear.is_active = gear.is_active + if gear.active is not None: + db_gear.active = gear.active if gear.initial_kms is not None: db_gear.initial_kms = gear.initial_kms if gear.purchase_value is not None: diff --git a/backend/app/gears/gear/models.py b/backend/app/gears/gear/models.py index 7364ad8bb..c4d2f9757 100644 --- a/backend/app/gears/gear/models.py +++ b/backend/app/gears/gear/models.py @@ -5,6 +5,7 @@ from sqlalchemy import ( DateTime, ForeignKey, DECIMAL, + Boolean, ) from sqlalchemy.orm import relationship from sqlalchemy.sql import func @@ -45,8 +46,8 @@ class Gear(Base): default=func.now(), comment="Gear creation date (DateTime)", ) - is_active = Column( - Integer, nullable=False, comment="Is gear active (0 - not active, 1 - active)" + active = Column( + Boolean, nullable=False, comment="Whether the gear is active (true - yes, false - no)" ) initial_kms = Column( DECIMAL(precision=11, scale=2), diff --git a/backend/app/gears/gear/schema.py b/backend/app/gears/gear/schema.py index 04e37c3e7..fdc096b17 100644 --- a/backend/app/gears/gear/schema.py +++ b/backend/app/gears/gear/schema.py @@ -8,7 +8,7 @@ class Gear(BaseModel): gear_type: int user_id: int | None = None created_at: str | None = None - is_active: int | None = None + active: bool | None = None initial_kms: float | None = None purchase_value: float | None = None strava_gear_id: str | None = None diff --git a/backend/app/gears/gear/utils.py b/backend/app/gears/gear/utils.py index d94cc6599..7358c6746 100644 --- a/backend/app/gears/gear/utils.py +++ b/backend/app/gears/gear/utils.py @@ -59,7 +59,7 @@ def transform_schema_gear_to_model_gear( gear_type=gear.gear_type, user_id=user_id, created_at=created_date, - is_active=gear.is_active, + active=gear.active, initial_kms=gear.initial_kms, purchase_value=gear.purchase_value, strava_gear_id=gear.strava_gear_id, diff --git a/backend/app/gears/gear_components/crud.py b/backend/app/gears/gear_components/crud.py index 9208d3138..d4d85a932 100644 --- a/backend/app/gears/gear_components/crud.py +++ b/backend/app/gears/gear_components/crud.py @@ -117,7 +117,7 @@ def create_gear_component( brand=gear_component.brand, model=gear_component.model, purchase_date=gear_component.purchase_date, - is_active=True, + active=True, expected_kms=gear_component.expected_kms, purchase_value=gear_component.purchase_value, ) diff --git a/backend/app/gears/gear_components/models.py b/backend/app/gears/gear_components/models.py index b4efe7f4d..d427a80ad 100644 --- a/backend/app/gears/gear_components/models.py +++ b/backend/app/gears/gear_components/models.py @@ -59,8 +59,11 @@ class GearComponents(Base): nullable=True, comment="Gear component retired date (DateTime)", ) - is_active = Column( - Boolean, nullable=False, default=False, comment="Is gear component active" + active = Column( + Boolean, + nullable=False, + default=False, + comment="Whether the gear component is active (true - yes, false - no)", ) expected_kms = Column( Integer, diff --git a/backend/app/gears/gear_components/schema.py b/backend/app/gears/gear_components/schema.py index 92bc2ef95..8967ff84a 100644 --- a/backend/app/gears/gear_components/schema.py +++ b/backend/app/gears/gear_components/schema.py @@ -97,7 +97,7 @@ class GearComponents(BaseModel): model (str): Model name or number of the gear component. purchase_date (str): Date when the component was purchased (ISO format recommended). retired_date (str | None): Date when the component was retired, if applicable. - is_active (bool | None): Indicates if the component is currently active. + active (bool | None): Indicates if the component is currently active. expected_kms (int | None): Expected kilometers the component should last. purchase_value (float | None): Purchase value of the component. """ @@ -109,7 +109,7 @@ class GearComponents(BaseModel): model: str purchase_date: str retired_date: str | None = None - is_active: bool | None = None + active: bool | None = None expected_kms: int | None = None purchase_value: float | None = None diff --git a/backend/app/password_reset_tokens/utils.py b/backend/app/password_reset_tokens/utils.py index 75d9213f8..88a8ba0a1 100644 --- a/backend/app/password_reset_tokens/utils.py +++ b/backend/app/password_reset_tokens/utils.py @@ -123,7 +123,7 @@ async def send_password_reset_email( 2. Attempts to locate the user record for the given email in the provided DB session. - For security (to avoid user enumeration), if the user does not exist the function returns True and does not indicate existence to the caller. - 3. Verifies the located user is active (expects user.is_active == 1). + 3. Verifies the located user is active. - If the user is inactive the function returns True for the same security reason. 4. Creates a password reset token and persists it via create_password_reset_token. 5. Constructs a frontend reset URL using the email_service.frontend_host and the token. @@ -175,7 +175,7 @@ async def send_password_reset_email( return True # Check if user is active - if user.is_active != 1: + if not user.active: # Don't reveal if user is inactive for security return True diff --git a/backend/app/session/router.py b/backend/app/session/router.py index 896530ea3..060532221 100644 --- a/backend/app/session/router.py +++ b/backend/app/session/router.py @@ -58,27 +58,29 @@ async def login_for_access_token( if profile_utils.is_mfa_enabled_for_user(user.id, db): # Store the user for pending MFA verification pending_mfa_store.add_pending_login(form_data.username, user.id) - + # Return MFA required response if client_type == "web": response.status_code = status.HTTP_202_ACCEPTED return session_schema.MFARequiredResponse( mfa_required=True, username=form_data.username, - message="MFA verification required" + message="MFA verification required", ) if client_type == "mobile": return { "mfa_required": True, "username": form_data.username, - "message": "MFA verification required" + "message": "MFA verification required", } - + # If no MFA required, proceed with normal login return await complete_login(response, request, user, client_type, db) -async def complete_login(response: Response, request: Request, user, client_type: str, db: Session): +async def complete_login( + response: Response, request: Request, user, client_type: str, db: Session +): # Create the tokens access_token, refresh_token, csrf_token = session_utils.create_tokens(user) @@ -127,17 +129,19 @@ async def signup( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Server settings not configured", ) - + # Check if signup is enabled users_utils.check_user_can_signup(server_settings) - + # Generate email verification token if needed email_verification_token = None if server_settings.signup_require_email_verification: email_verification_token = users_utils.generate_email_verification_token() - + # Create the user in the database - created_user = users_crud.create_signup_user(user, email_verification_token, server_settings, db) + created_user = users_crud.create_signup_user( + user, email_verification_token, server_settings, db + ) # Create the user integrations in the database user_integrations_crud.create_user_integrations(created_user.id, db) @@ -153,19 +157,26 @@ async def signup( # Return appropriate response based on server configuration response_data = {"message": "User created successfully"} - + if server_settings.signup_require_email_verification: - response_data["message"] = "User created successfully. Please check your email for verification instructions." + response_data["message"] = ( + "User created successfully. Please check your email for verification instructions." + ) response_data["email_verification_required"] = True # TODO: Send verification email here - + if server_settings.signup_require_admin_approval: - response_data["message"] = "User created successfully. Account is pending admin approval." + response_data["message"] = ( + "User created successfully. Account is pending admin approval." + ) response_data["admin_approval_required"] = True - - if not server_settings.signup_require_email_verification and not server_settings.signup_require_admin_approval: + + if ( + not server_settings.signup_require_email_verification + and not server_settings.signup_require_admin_approval + ): response_data["message"] = "User created successfully. You can now log in." - + return response_data @@ -185,17 +196,19 @@ async def verify_email( status_code=status.HTTP_404_NOT_FOUND, detail="Email verification is not enabled", ) - + # Verify the email user = users_crud.verify_user_email(token, db) - + message = "Email verified successfully." if user.pending_admin_approval: message += " Your account is now pending admin approval." else: message += " You can now log in." - - return {"message": message, "user_active": user.is_active == session_constants.USER_ACTIVE} + + return { + "message": message, + } @router.post("/mfa/verify") @@ -217,31 +230,29 @@ async def verify_mfa_and_login( if not user_id: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, - detail="No pending MFA login found for this username" + detail="No pending MFA login found for this username", ) - + # Verify the MFA code if not profile_utils.verify_user_mfa(user_id, mfa_request.mfa_code, db): raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Invalid MFA code" + status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid MFA code" ) - + # Get the user and complete login user = users_crud.get_user_by_id(user_id, db) if not user: pending_mfa_store.delete_pending_login(mfa_request.username) raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="User not found" + status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) - + # Check if the user is still active users_utils.check_user_is_active(user) - + # Clean up pending login pending_mfa_store.delete_pending_login(mfa_request.username) - + # Complete the login return await complete_login(response, request, user, client_type, db) diff --git a/backend/app/session/security.py b/backend/app/session/security.py index 953da3016..25d0240e1 100644 --- a/backend/app/session/security.py +++ b/backend/app/session/security.py @@ -112,7 +112,7 @@ def validate_token_expiration(token: Annotated[str, Depends(oauth2_scheme)]) -> # Validate token expiration claims_requests.validate(payload.claims) - except jwt.JWTClaimsError as claims_err: + except jwt.InvalidClaimError as claims_err: core_logger.print_to_log( f"JWT claims validation error: {claims_err}", "error", diff --git a/backend/app/strava/gear_utils.py b/backend/app/strava/gear_utils.py index f5ddebee8..759961da3 100644 --- a/backend/app/strava/gear_utils.py +++ b/backend/app/strava/gear_utils.py @@ -101,7 +101,7 @@ def process_gear( nickname=strava_gear.name, gear_type=1 if type == "bike" else 2, user_id=user_id, - is_active=1, + active=True, strava_gear_id=gear.id, ) diff --git a/backend/app/users/user/crud.py b/backend/app/users/user/crud.py index 1690b82eb..4c30d5dba 100644 --- a/backend/app/users/user/crud.py +++ b/backend/app/users/user/crud.py @@ -586,21 +586,21 @@ def create_signup_user(user: users_schema.UserSignup, email_verification_token: """Create a new user via sign-up with appropriate verification and approval settings""" try: # Determine user status based on server settings - is_active = session_constants.USER_ACTIVE + active = True email_verified = False pending_admin_approval = False if server_settings.signup_require_email_verification: email_verified = False - is_active = session_constants.USER_NOT_ACTIVE # Inactive until email verified - + active = False # Inactive until email verified + if server_settings.signup_require_admin_approval: pending_admin_approval = True - is_active = session_constants.USER_NOT_ACTIVE # Inactive until approved - + active = False # Inactive until approved + # If both email verification and admin approval are disabled, user is immediately active if not server_settings.signup_require_email_verification and not server_settings.signup_require_admin_approval: - is_active = session_constants.USER_ACTIVE + active = True email_verified = True # Create a new user @@ -614,8 +614,8 @@ def create_signup_user(user: users_schema.UserSignup, email_verification_token: gender=user.gender, units=user.units, height=user.height, - access_type=session_constants.REGULAR_ACCESS, - is_active=is_active, + access_type=users_schema.UserAccessType.REGULAR, + active=active, first_day_of_week=user.first_day_of_week, currency=user.currency, email_verified=email_verified, @@ -676,7 +676,7 @@ def verify_user_email(token: str, db: Session): # If not pending admin approval, activate the user if not db_user.pending_admin_approval: - db_user.is_active = session_constants.USER_ACTIVE + db_user.active = True db.commit() db.refresh(db_user) diff --git a/backend/app/users/user/models.py b/backend/app/users/user/models.py index c11120fef..7ba559734 100644 --- a/backend/app/users/user/models.py +++ b/backend/app/users/user/models.py @@ -56,8 +56,11 @@ class User(Base): Integer, nullable=False, comment="User type (one digit)(1 - user, 2 - admin)" ) photo_path = Column(String(length=250), nullable=True, comment="User photo path") - is_active = Column( - Integer, nullable=False, comment="Is user active (1 - active, 2 - not active)" + active = Column( + Boolean, + nullable=False, + default=True, + comment="Whether the user is active (true - yes, false - no)", ) first_day_of_week = Column( Integer, diff --git a/backend/app/users/user/schema.py b/backend/app/users/user/schema.py index 24398bcc7..1ce9fc304 100644 --- a/backend/app/users/user/schema.py +++ b/backend/app/users/user/schema.py @@ -1,5 +1,39 @@ -from pydantic import BaseModel, EmailStr, field_validator +from enum import Enum, IntEnum +from pydantic import BaseModel, EmailStr, field_validator, StrictInt, ConfigDict import re +import server_settings.schema as server_settings_schema + + +class Gender(IntEnum): + MALE = 1 + FEMALE = 2 + UNSPECIFIED = 3 + + +class Language(Enum): + CATALAN = "ca" + DUTCH = "nl" + GERMAN = "de" + FRENCH = "fr" + SPANISH = "es" + PORTUGUESE = "pt" + ENGLISH_USA = "us" + + +class WeekDay(IntEnum): + SUNDAY = 0 + MONDAY = 1 + TUESDAY = 2 + WEDNESDAY = 3 + THURSDAY = 4 + FRIDAY = 5 + SATURDAY = 6 + + +class UserAccessType(IntEnum): + REGULAR = 1 + ADMIN = 2 + PASSWORD_REGEX = r"^(?=.*[A-Z])(?=.*\d)(?=.*[ !\"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])[A-Za-z\d !\"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]{8,}$" @@ -12,37 +46,34 @@ def validate_password(value: str) -> str: return value -class User(BaseModel): - id: int | None = None +class UserBase(BaseModel): name: str username: str email: EmailStr city: str | None = None birthdate: str | None = None - preferred_language: str - gender: int - units: int + preferred_language: Language = Language.ENGLISH_USA + gender: Gender = Gender.MALE + units: server_settings_schema.Units = server_settings_schema.Units.METRIC height: int | None = None - access_type: int + first_day_of_week: WeekDay = WeekDay.MONDAY + currency: server_settings_schema.Currency = server_settings_schema.Currency.EURO + + +class User(UserBase): + id: StrictInt + access_type: UserAccessType photo_path: str | None = None - is_active: int - first_day_of_week: int = 1 - currency: int + active: bool mfa_enabled: bool = False mfa_secret: str | None = None email_verified: bool = False email_verification_token: str | None = None pending_admin_approval: bool = False - model_config = {"from_attributes": True} - - -class UserCreate(User): - password: str - - @field_validator("password") - def validate_password_field(cls, value): - return validate_password(value) + model_config = ConfigDict( + from_attributes=True, extra="forbid", validate_assignment=True + ) class UserMe(User): @@ -63,28 +94,26 @@ class UserMe(User): hide_activity_gear: bool | None = None +class UserSignup(UserBase): + password: str + + @field_validator("password") + def validate_password_field(cls, value): + return validate_password(value) + + +class UserCreate(User): + id: StrictInt + password: str + + @field_validator("password") + def validate_password_field(cls, value): + return validate_password(value) + + class UserEditPassword(BaseModel): password: str @field_validator("password") def validate_password_field(cls, value): return validate_password(value) - - -class UserSignup(BaseModel): - name: str - username: str - email: EmailStr - city: str | None = None - birthdate: str | None = None - preferred_language: str = "en" - gender: int = 1 # Default to male - units: int = 1 # Default to metric - height: int | None = None - first_day_of_week: int = 1 - currency: int = 1 # Default to euro - password: str - - @field_validator("password") - def validate_password_field(cls, value): - return validate_password(value) diff --git a/backend/app/users/user/utils.py b/backend/app/users/user/utils.py index 52b701e47..f7e7eb86d 100644 --- a/backend/app/users/user/utils.py +++ b/backend/app/users/user/utils.py @@ -17,7 +17,7 @@ import core.config as core_config def check_user_is_active(user: users_schema.User) -> None: - if user.is_active == session_constants.USER_NOT_ACTIVE: + if not user.active: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Inactive user", diff --git a/frontend/app/src/components/Gears/GearComponentAddEditModalComponent.vue b/frontend/app/src/components/Gears/GearComponentAddEditModalComponent.vue index 4b51b3fef..6a2fc0e6f 100644 --- a/frontend/app/src/components/Gears/GearComponentAddEditModalComponent.vue +++ b/frontend/app/src/components/Gears/GearComponentAddEditModalComponent.vue @@ -268,7 +268,7 @@ class="form-select" name="gearComponentIsActiveAddEdit" :disabled="newEditGearComponentRetiredDate" - v-model="newEditGearComponentIsActive" + v-model="newEditGearComponentActive" required > @@ -359,7 +359,7 @@ const newEditGearComponentExpectedDistanceMiles = ref(null) const newEditGearComponentExpectedTime = ref(null) const newEditGearComponentPurchaseValue = ref(null) const newEditGearComponentRetiredDate = ref(null) -const newEditGearComponentIsActive = ref(true) +const newEditGearComponentActive = ref(true) onMounted(() => { newEditGearComponentUserId.value = props.gear.user_id @@ -384,7 +384,7 @@ onMounted(() => { } newEditGearComponentPurchaseValue.value = props.gearComponent.purchase_value newEditGearComponentRetiredDate.value = props.gearComponent.retired_date - newEditGearComponentIsActive.value = props.gearComponent.is_active + newEditGearComponentActive.value = props.gearComponent.active } else { if (props.gear.gear_type === 1) { newEditGearComponentType.value = 'back_break_oil' @@ -400,10 +400,10 @@ onMounted(() => { function updateIsActiveBasedOnRetiredDate() { if (newEditGearComponentRetiredDate.value && newEditGearComponentRetiredDate.value !== '') { - newEditGearComponentIsActive.value = false + newEditGearComponentActive.value = false } else { newEditGearComponentRetiredDate.value = null - newEditGearComponentIsActive.value = true + newEditGearComponentActive.value = true } } @@ -479,7 +479,7 @@ async function submitEditGearComponentForm() { retired_date: newEditGearComponentRetiredDate.value ? newEditGearComponentRetiredDate.value : null, - is_active: newEditGearComponentIsActive.value, + active: newEditGearComponentActive.value, expected_kms: expected_kms, purchase_value: newEditGearComponentPurchaseValue.value } diff --git a/frontend/app/src/components/Gears/GearComponentListComponent.vue b/frontend/app/src/components/Gears/GearComponentListComponent.vue index 116e58e32..b0e9c6e59 100644 --- a/frontend/app/src/components/Gears/GearComponentListComponent.vue +++ b/frontend/app/src/components/Gears/GearComponentListComponent.vue @@ -116,7 +116,7 @@ /> -
    +
    {{ $t('gearComponentListComponent.gearComponentListGearComponentIsInactiveBadge') }} diff --git a/frontend/app/src/components/Gears/GearsAddEditGearModalComponent.vue b/frontend/app/src/components/Gears/GearsAddEditGearModalComponent.vue index 0879ee6ea..2388ccd37 100644 --- a/frontend/app/src/components/Gears/GearsAddEditGearModalComponent.vue +++ b/frontend/app/src/components/Gears/GearsAddEditGearModalComponent.vue @@ -119,22 +119,22 @@ v-model="newEditGearCreatedDate" required /> - -
    - +
    -
    @@ -524,6 +524,7 @@ const togglePasswordVisibility = () => { } if (props.user) { + console.log(props.user) if (props.action === 'edit') { editUserModalId.value = `editUserModal${props.user.id}` } diff --git a/frontend/app/src/components/Settings/SettingsUsersZone/UsersListComponent.vue b/frontend/app/src/components/Settings/SettingsUsersZone/UsersListComponent.vue index 42df50e84..39d2d3f81 100644 --- a/frontend/app/src/components/Settings/SettingsUsersZone/UsersListComponent.vue +++ b/frontend/app/src/components/Settings/SettingsUsersZone/UsersListComponent.vue @@ -19,8 +19,10 @@ v-if="user.id == authStore.user.id">{{ $t('usersListComponent.userListUserIsMeBadge') }} {{ $t('usersListComponent.userListUserIsAdminBadge') }} - {{ $t('usersListComponent.userListUserIsInactiveBadge') }} + {{ $t('usersListComponent.userListUserHasUnverifiedEmailBadge') }} @@ -40,18 +42,18 @@ :title="t('usersListComponent.modalApproveSignUpTitle')" :body="`${t('usersListComponent.modalApproveSignUpBody')}${user.username}?`" :actionButtonType="`success`" :actionButtonText="t('usersListComponent.modalApproveSignUpTitle')" - @submitAction="submitApproveSignUp" v-if="user.pending_admin_approval" /> + @submitAction="submitApproveSignUp" v-if="user.pending_admin_approval && user.email_verified" /> + @submitAction="submitDeleteUser" v-if="user.pending_admin_approval && user.email_verified" /> { } try { - const response = await session.verifyEmail(token) - success.value = true - message.value = response.message || t('emailVerification.verificationSuccess') - + await signUpService.verifyEmail({ + token: token + }) push.success(t('emailVerification.emailVerified')) + router.push('/login?verifyEmail=true') } catch (error) { - success.value = false - if (error.toString().includes('404')) { - message.value = t('emailVerification.tokenNotFound') + push.error(`${t('emailVerification.tokenNotFound')} - ${error}`) } else if (error.toString().includes('400')) { - message.value = t('emailVerification.tokenExpired') + push.error(`${t('emailVerification.tokenExpired')} - ${error}`) } else { - message.value = t('emailVerification.verificationFailed') + push.error(`${t('emailVerification.verificationFailed')} - ${error}`) } - - push.error(message.value) - } finally { - loading.value = false } }) \ No newline at end of file From 5390c8a30ade99f1039f65fcd9b1b2104e8f7b37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vit=C3=B3ria=20Silva?= <8648976+joaovitoriasilva@users.noreply.github.com> Date: Thu, 18 Sep 2025 10:24:40 +0100 Subject: [PATCH 54/92] Update env and compose examples, fix distance formatting Improved comments and guidance in .env.example for DB variables, clarified volume path in docker-compose.yml.example #336 Fixed formatDistanceRaw usage in UserGoalsStatsComponent.vue by adding the missing argument. --- .env.example | 10 ++++++---- docker-compose.yml.example | 2 +- .../src/components/Users/UserGoalsStatsComponent.vue | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index d467104b8..ea62901e0 100644 --- a/.env.example +++ b/.env.example @@ -2,15 +2,17 @@ # These are just the variable you have to set to be up and running. # There is many more variable you could set. Check them out here: https://docs.endurain.com/getting-started/advanced-started/#supported-environment-variables -DB_PASSWORD=changeme -POSTGRES_PASSWORD=changeme +DB_PASSWORD=changeme # Set a strong password here. Check if there are no trailing whitespaces in the beginning and end. Must be the same as POSTGRES_PASSWORD +POSTGRES_PASSWORD=changeme # Must be the same as DB_PASSWORD SECRET_KEY=changeme FERNET_KEY=changeme TZ=Europe/Lisbon ENDURAIN_HOST=https://endurain.example.com BEHIND_PROXY=true -POSTGRES_DB=endurain -POSTGRES_USER=endurain +POSTGRES_DB=endurain # If you change this, you also have to change DB_DATABASE +# DB_DATABASE=endurain # Uncomment and set it to the same as POSTGRES_DB if you change it +POSTGRES_USER=endurain # If you change this, you also have to change DB_USER +# DB_USER=endurain # Uncomment and set it to the same as POSTGRES_USER if you change it PGDATA=/var/lib/postgresql/data/pgdata # Email configuration (for password reset functionality) diff --git a/docker-compose.yml.example b/docker-compose.yml.example index 11886556d..e284979fb 100644 --- a/docker-compose.yml.example +++ b/docker-compose.yml.example @@ -27,5 +27,5 @@ services: timeout: 5s retries: 5 volumes: - - /opt/endurain/postgres:/var/lib/postgresql/data + - /endurain/postgres:/var/lib/postgresql/data restart: unless-stopped diff --git a/frontend/app/src/components/Users/UserGoalsStatsComponent.vue b/frontend/app/src/components/Users/UserGoalsStatsComponent.vue index a43bcbbd3..f3b856cdb 100644 --- a/frontend/app/src/components/Users/UserGoalsStatsComponent.vue +++ b/frontend/app/src/components/Users/UserGoalsStatsComponent.vue @@ -43,7 +43,7 @@ }}{{ goal.goal_activities_number }} {{ $t('userGoalsStatsComponent.activities') }} {{ formatDistanceRaw(t, goal.total_distance, authStore.user.units) + >{{ formatDistanceRaw(t, goal.total_distance, authStore.user.units, false) }}{{ $t('generalItems.ofWithSpaces') }}{{ formatDistanceRaw(t, goal.goal_distance, authStore.user.units) }} From 0c6672a1873b7ae2c9c85c4382df8880dc226742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vit=C3=B3ria=20Silva?= <8648976+joaovitoriasilva@users.noreply.github.com> Date: Thu, 18 Sep 2025 11:21:44 +0100 Subject: [PATCH 55/92] Improve email verification flow and messaging Refactors backend to return more detailed responses for email verification, including admin approval requirements. Updates frontend to handle new response structure, display improved messages, and redirect users appropriately. Adds new i18n strings for various verification outcomes. --- backend/app/sign_up_tokens/crud.py | 122 +++++++++--------- backend/app/sign_up_tokens/router.py | 21 +-- .../src/i18n/us/emailVerificationView.json | 6 +- .../app/src/views/EmailVerificationView.vue | 14 +- 4 files changed, 88 insertions(+), 75 deletions(-) diff --git a/backend/app/sign_up_tokens/crud.py b/backend/app/sign_up_tokens/crud.py index 81d4de6da..d4145c67f 100644 --- a/backend/app/sign_up_tokens/crud.py +++ b/backend/app/sign_up_tokens/crud.py @@ -9,6 +9,67 @@ import sign_up_tokens.models as sign_up_tokens_models import core.logger as core_logger +def get_sign_up_token_by_hash( + token_hash: str, db: Session +) -> sign_up_tokens_models.SignUpToken | None: + """ + Retrieve an unused, unexpired SignUpToken matching the provided token hash. + + Parameters + ---------- + token_hash : str + The hashed token value to look up in the database. + db : Session + The SQLAlchemy Session used to perform the query. + + Returns + ------- + sign_up_tokens_models.SignUpToken | None + The SignUpToken model instance if a matching token exists, is not marked as used, + and has an expires_at timestamp later than the current UTC time. Returns None when + no valid token is found. + + Raises + ------ + HTTPException + If an unexpected error occurs during the database query, the exception is logged + and an HTTPException with status code 500 (Internal Server Error) is raised. + + Notes + ----- + - The function filters tokens by: token_hash equality, used == False, and expires_at > now (UTC). + - Any caught exception is logged via core_logger.print_to_log before raising the HTTPException. + """ + try: + # Get the token from the database + db_token = ( + db.query(sign_up_tokens_models.SignUpToken) + .filter( + and_( + sign_up_tokens_models.SignUpToken.token_hash == token_hash, + sign_up_tokens_models.SignUpToken.used == False, + sign_up_tokens_models.SignUpToken.expires_at + > datetime.now(timezone.utc), + ) + ) + .first() + ) + + # Return the token (can be None if not found) + return db_token + except Exception as err: + # Log the exception + core_logger.print_to_log( + f"Error in get_sign_up_token_by_hash: {err}", "error", exc=err + ) + + # Raise an HTTPException with a 500 Internal Server Error status code + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Internal Server Error", + ) from err + + def create_sign_up_token( token: sign_up_tokens_schema.SignUpToken, db: Session ) -> sign_up_tokens_models.SignUpToken: @@ -76,67 +137,6 @@ def create_sign_up_token( ) from err -def get_sign_up_token_by_hash( - token_hash: str, db: Session -) -> sign_up_tokens_models.SignUpToken | None: - """ - Retrieve an unused, unexpired SignUpToken matching the provided token hash. - - Parameters - ---------- - token_hash : str - The hashed token value to look up in the database. - db : Session - The SQLAlchemy Session used to perform the query. - - Returns - ------- - sign_up_tokens_models.SignUpToken | None - The SignUpToken model instance if a matching token exists, is not marked as used, - and has an expires_at timestamp later than the current UTC time. Returns None when - no valid token is found. - - Raises - ------ - HTTPException - If an unexpected error occurs during the database query, the exception is logged - and an HTTPException with status code 500 (Internal Server Error) is raised. - - Notes - ----- - - The function filters tokens by: token_hash equality, used == False, and expires_at > now (UTC). - - Any caught exception is logged via core_logger.print_to_log before raising the HTTPException. - """ - try: - # Get the token from the database - db_token = ( - db.query(sign_up_tokens_models.SignUpToken) - .filter( - and_( - sign_up_tokens_models.SignUpToken.token_hash == token_hash, - sign_up_tokens_models.SignUpToken.used == False, - sign_up_tokens_models.SignUpToken.expires_at - > datetime.now(timezone.utc), - ) - ) - .first() - ) - - # Return the token (can be None if not found) - return db_token - except Exception as err: - # Log the exception - core_logger.print_to_log( - f"Error in get_sign_up_token_by_hash: {err}", "error", exc=err - ) - - # Raise an HTTPException with a 500 Internal Server Error status code - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Internal Server Error", - ) from err - - def mark_sign_up_token_used( token_id: str, db: Session ) -> sign_up_tokens_models.SignUpToken | None: diff --git a/backend/app/sign_up_tokens/router.py b/backend/app/sign_up_tokens/router.py index 23d6f56da..266333c7a 100644 --- a/backend/app/sign_up_tokens/router.py +++ b/backend/app/sign_up_tokens/router.py @@ -166,20 +166,21 @@ async def verify_email( server_settings = server_settings_utils.get_server_settings(db) if not server_settings.signup_require_email_verification: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, + status_code=status.HTTP_412_PRECONDITION_FAILED, detail="Email verification is not enabled", ) # Verify the email - server_settings = server_settings_utils.get_server_settings(db) - await sign_up_tokens_utils.use_sign_up_token(confirm_data.token, server_settings, db) + await sign_up_tokens_utils.use_sign_up_token( + confirm_data.token, server_settings, db + ) - message = "Email verified successfully." - if user.pending_admin_approval: - message += " Your account is now pending admin approval." + # Return appropriate response based on server configuration + response_data = {"message": "Email verified successfully."} + if server_settings.signup_require_admin_approval: + response_data["message"] += " Your account is now pending admin approval." + response_data["admin_approval_required"] = True else: - message += " You can now log in." + response_data["message"] += " You can now log in." - return { - "message": message, - } + return response_data diff --git a/frontend/app/src/i18n/us/emailVerificationView.json b/frontend/app/src/i18n/us/emailVerificationView.json index a2d0f7c7d..7815a375d 100644 --- a/frontend/app/src/i18n/us/emailVerificationView.json +++ b/frontend/app/src/i18n/us/emailVerificationView.json @@ -1,4 +1,8 @@ { "title1": "Handling verify email", - "title2": "Please wait while your email is being verified. Do not refresh this page." + "title2": "Please wait while your email is being verified. Do not refresh this page.", + "emailVerified": "Email verified successfully!", + "tokenNotFound": "Token not found", + "tokenExpired": "Token expired", + "verificationFailed": "Email verification failed" } \ No newline at end of file diff --git a/frontend/app/src/views/EmailVerificationView.vue b/frontend/app/src/views/EmailVerificationView.vue index 387b2d22f..2019b80cf 100644 --- a/frontend/app/src/views/EmailVerificationView.vue +++ b/frontend/app/src/views/EmailVerificationView.vue @@ -8,7 +8,7 @@ +``` + +Or use the Composition API with ` +``` + +### Type Checking + +Run type checking manually: + +```bash +npm run type-check +``` + +The build process (`npm run build`) now includes type checking automatically. + +### IDE Support + +TypeScript configuration is provided via `tsconfig.json`, which enables: +- IntelliSense and autocompletion +- Type checking in your IDE +- Better refactoring support +- Import path resolution for `@/` alias + +## Configuration Files + +- `tsconfig.json` - Root configuration with project references +- `tsconfig.app.json` - Configuration for application code +- `tsconfig.node.json` - Configuration for Node.js build tools (Vite, Vitest) +- `tsconfig.vitest.json` - Configuration for test files +- `src/env.d.ts` - Vite and global type declarations + +## Migration Strategy + +TypeScript is **optional** and migration can be done incrementally: + +1. Start with new features - write new components/utilities in TypeScript +2. Gradually migrate existing files when making significant changes +3. No immediate action required for existing JavaScript files + +## Important Notes + +- `allowJs: true` is enabled, so JavaScript and TypeScript can coexist +- Existing JavaScript files will not show type errors +- Type checking is strict for TypeScript files but permissive for JavaScript diff --git a/frontend/app/jsconfig.json b/frontend/app/jsconfig.json deleted file mode 100644 index 5a1f2d222..000000000 --- a/frontend/app/jsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "paths": { - "@/*": ["./src/*"] - } - }, - "exclude": ["node_modules", "dist"] -} diff --git a/frontend/app/package-lock.json b/frontend/app/package-lock.json index 9091b773c..ff341db5b 100644 --- a/frontend/app/package-lock.json +++ b/frontend/app/package-lock.json @@ -28,17 +28,26 @@ "devDependencies": { "@rushstack/eslint-patch": "^1.10.4", "@types/bootstrap": "^5.2.10", + "@types/jsdom": "^27.0.0", "@types/leaflet": "^1.9.14", + "@types/node": "^24.6.1", + "@typescript-eslint/eslint-plugin": "^8.45.0", + "@typescript-eslint/parser": "^8.45.0", "@vitejs/plugin-vue": "^5.2.1", "@vue/eslint-config-prettier": "^10.1.0", + "@vue/eslint-config-typescript": "^14.6.0", "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.8.1", "eslint": "^9.15.0", "eslint-plugin-vue": "^10.3.0", "jsdom": "^26.0.0", + "npm-run-all2": "^8.0.4", + "typescript": "^5.9.3", "vite": "^6.1.0", "vite-plugin-pwa": "^1.0.0", "vitest": "^3.0.5", - "vue-eslint-parser": "^10.2.0" + "vue-eslint-parser": "^10.2.0", + "vue-tsc": "^3.1.0" } }, "node_modules/@asamuzakjp/css-color": { @@ -2587,6 +2596,44 @@ "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", "license": "MIT" }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@one-ini/wasm": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", @@ -3071,6 +3118,18 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsdom": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-27.0.0.tgz", + "integrity": "sha512-NZyFl/PViwKzdEkQg96gtnB8wm+1ljhdDay9ahn4hgb+SfVtPCbm3TlmDUFXTA+MGN3CijicnMhG18SI5H3rFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3088,6 +3147,16 @@ "@types/geojson": "*" } }, + "node_modules/@types/node": { + "version": "24.6.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.1.tgz", + "integrity": "sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.13.0" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -3095,6 +3164,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -3102,6 +3178,264 @@ "dev": true, "license": "MIT" }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/type-utils": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.45.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.45.0", + "@typescript-eslint/types": "^8.45.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vitejs/plugin-vue": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", @@ -3231,6 +3565,35 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@volar/language-core": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz", + "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.23" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz", + "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz", + "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, "node_modules/@vue/compiler-core": { "version": "3.5.21", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", @@ -3353,6 +3716,56 @@ "prettier": ">= 3.0.0" } }, + "node_modules/@vue/eslint-config-typescript": { + "version": "14.6.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.6.0.tgz", + "integrity": "sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.35.1", + "fast-glob": "^3.3.3", + "typescript-eslint": "^8.35.1", + "vue-eslint-parser": "^10.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0", + "eslint-plugin-vue": "^9.28.0 || ^10.0.0", + "typescript": ">=4.8.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.1.0.tgz", + "integrity": "sha512-a7ns+X9vTbdmk7QLrvnZs8s4E1wwtxG/sELzr6F2j4pU+r/OoAv6jJGSz+5tVTU6e4+3rjepGhSP8jDmBBcb3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.0.0", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@vue/reactivity": { "version": "3.5.21", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz", @@ -3414,6 +3827,25 @@ "vue-component-type-helpers": "^2.0.0" } }, + "node_modules/@vue/tsconfig": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.8.1.tgz", + "integrity": "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, "node_modules/abbrev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", @@ -3474,6 +3906,13 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/alien-signals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.0.0.tgz", + "integrity": "sha512-JHoRJf18Y6HN4/KZALr3iU+0vW9LKG+8FMThQlbn4+gv8utsLIkwpomjElGPccGeNwh0FI2HN6BLnyFLo6OyLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -3707,6 +4146,19 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.25.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz", @@ -4793,6 +5245,36 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -4824,6 +5306,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -4888,6 +5380,19 @@ "node": ">=10" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -5228,6 +5733,13 @@ "dev": true, "license": "ISC" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -5703,6 +6215,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-number-object": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", @@ -6087,6 +6609,16 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -6270,6 +6802,52 @@ "node": ">= 0.4" } }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -6306,6 +6884,13 @@ "dev": true, "license": "MIT" }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -6376,6 +6961,82 @@ } } }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz", + "integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "picomatch": "^4.0.2", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^20.5.0 || >=22.0.0", + "npm": ">= 10" + } + }, + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -6551,6 +7212,13 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6647,6 +7315,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/pinia": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.3.tgz", @@ -6790,6 +7471,27 @@ "node": ">=6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -6800,6 +7502,20 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -6956,6 +7672,17 @@ "node": ">=4" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -7010,6 +7737,30 @@ "dev": true, "license": "MIT" }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -7201,6 +7952,19 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -7861,6 +8625,19 @@ "dev": true, "license": "MIT" }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/tough-cookie": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", @@ -7887,6 +8664,19 @@ "node": ">=18" } }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -7991,6 +8781,44 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.45.0", + "@typescript-eslint/parser": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -8010,6 +8838,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", + "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", @@ -8338,6 +9173,13 @@ } } }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, "node_modules/vue": { "version": "3.5.21", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", @@ -8437,6 +9279,23 @@ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", "license": "MIT" }, + "node_modules/vue-tsc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.1.0.tgz", + "integrity": "sha512-fbMynMG7kXSnqZTRBSCh9ROYaVpXfCZbEO0gY3lqOjLbp361uuS88n6BDajiUriDIF+SGLWoinjvf6stS2J3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.23", + "@vue/language-core": "3.1.0" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", diff --git a/frontend/app/package.json b/frontend/app/package.json index 139782c57..320fe55c0 100644 --- a/frontend/app/package.json +++ b/frontend/app/package.json @@ -5,10 +5,12 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vite build", + "build": "run-p type-check \"build-only {@}\" --", + "build-only": "vite build", "preview": "vite preview", "test:unit": "vitest", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore", + "type-check": "vue-tsc --build --force", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx --fix --ignore-path .gitignore", "format": "prettier --write src/" }, "dependencies": { @@ -32,16 +34,25 @@ "devDependencies": { "@rushstack/eslint-patch": "^1.10.4", "@types/bootstrap": "^5.2.10", + "@types/jsdom": "^27.0.0", "@types/leaflet": "^1.9.14", + "@types/node": "^24.6.1", + "@typescript-eslint/eslint-plugin": "^8.45.0", + "@typescript-eslint/parser": "^8.45.0", "@vitejs/plugin-vue": "^5.2.1", "@vue/eslint-config-prettier": "^10.1.0", + "@vue/eslint-config-typescript": "^14.6.0", "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.8.1", "eslint": "^9.15.0", "eslint-plugin-vue": "^10.3.0", "jsdom": "^26.0.0", + "npm-run-all2": "^8.0.4", + "typescript": "^5.9.3", "vite": "^6.1.0", "vite-plugin-pwa": "^1.0.0", "vitest": "^3.0.5", - "vue-eslint-parser": "^10.2.0" + "vue-eslint-parser": "^10.2.0", + "vue-tsc": "^3.1.0" } } diff --git a/frontend/app/src/components/TypeScriptTestComponent.vue b/frontend/app/src/components/TypeScriptTestComponent.vue new file mode 100644 index 000000000..a306082bd --- /dev/null +++ b/frontend/app/src/components/TypeScriptTestComponent.vue @@ -0,0 +1,21 @@ + + + diff --git a/frontend/app/src/env.d.ts b/frontend/app/src/env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/frontend/app/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/frontend/app/src/utils/typeTest.ts b/frontend/app/src/utils/typeTest.ts new file mode 100644 index 000000000..6c3d6d37e --- /dev/null +++ b/frontend/app/src/utils/typeTest.ts @@ -0,0 +1,14 @@ +// Simple test file to verify TypeScript support +export function greet(name: string): string { + return `Hello, ${name}!` +} + +export interface User { + id: number + name: string + email?: string +} + +export const createUser = (id: number, name: string, email?: string): User => { + return { id, name, email } +} diff --git a/frontend/app/tsconfig.app.json b/frontend/app/tsconfig.app.json new file mode 100644 index 000000000..9e7d37095 --- /dev/null +++ b/frontend/app/tsconfig.app.json @@ -0,0 +1,15 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + "allowJs": true, + "skipLibCheck": true + } +} diff --git a/frontend/app/tsconfig.json b/frontend/app/tsconfig.json new file mode 100644 index 000000000..4b55668b8 --- /dev/null +++ b/frontend/app/tsconfig.json @@ -0,0 +1,14 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.vitest.json" + } + ] +} diff --git a/frontend/app/tsconfig.node.json b/frontend/app/tsconfig.node.json new file mode 100644 index 000000000..1365c82f9 --- /dev/null +++ b/frontend/app/tsconfig.node.json @@ -0,0 +1,18 @@ +{ + "extends": "@vue/tsconfig/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + ".eslintrc.cjs" + ], + "compilerOptions": { + "composite": true, + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "allowJs": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "types": ["node"] + } +} diff --git a/frontend/app/tsconfig.vitest.json b/frontend/app/tsconfig.vitest.json new file mode 100644 index 000000000..e56c5a8bb --- /dev/null +++ b/frontend/app/tsconfig.vitest.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.app.json", + "exclude": [], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", + "lib": [], + "types": ["node", "jsdom"] + } +} From e76cd9946d5dd5d6afd039218c5480ef4347430a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Oct 2025 22:11:49 +0000 Subject: [PATCH 73/92] Remove test files and add starter type definitions Co-authored-by: joaovitoriasilva <8648976+joaovitoriasilva@users.noreply.github.com> --- frontend/app/TYPESCRIPT.md | 69 +++++++++++++++++++ .../components/TypeScriptTestComponent.vue | 21 ------ frontend/app/src/types/index.ts | 15 ++++ frontend/app/src/utils/typeTest.ts | 14 ---- 4 files changed, 84 insertions(+), 35 deletions(-) delete mode 100644 frontend/app/src/components/TypeScriptTestComponent.vue create mode 100644 frontend/app/src/types/index.ts delete mode 100644 frontend/app/src/utils/typeTest.ts diff --git a/frontend/app/TYPESCRIPT.md b/frontend/app/TYPESCRIPT.md index 5c858b821..54c25562d 100644 --- a/frontend/app/TYPESCRIPT.md +++ b/frontend/app/TYPESCRIPT.md @@ -75,6 +75,16 @@ npm run type-check The build process (`npm run build`) now includes type checking automatically. +### Linting + +TypeScript files are configured to work with ESLint: + +```bash +npm run lint +``` + +Note: The project uses ESLint 9 which requires migration from `.eslintrc.cjs` to `eslint.config.js`. The current ESLint configuration may need updates for full functionality. + ### IDE Support TypeScript configuration is provided via `tsconfig.json`, which enables: @@ -99,8 +109,67 @@ TypeScript is **optional** and migration can be done incrementally: 2. Gradually migrate existing files when making significant changes 3. No immediate action required for existing JavaScript files +## Practical Examples + +### Migrating a JavaScript Utility to TypeScript + +Before (JavaScript): +```javascript +// src/utils/example.js +export function calculateDistance(lat1, lon1, lat2, lon2) { + // ... implementation + return distance +} +``` + +After (TypeScript): +```typescript +// src/utils/example.ts +export function calculateDistance( + lat1: number, + lon1: number, + lat2: number, + lon2: number +): number { + // ... implementation + return distance +} +``` + +### Using Existing JavaScript Files from TypeScript + +TypeScript files can import and use existing JavaScript modules: + +```typescript +// Import from existing JS service +import { fetchUserProfile } from '@/services/profileService' + +// TypeScript provides type safety for your new code +const profile = await fetchUserProfile(userId) +``` + +### Defining Types for API Responses + +```typescript +// src/types/api.ts +export interface Activity { + id: number + name: string + distance: number + duration: number + date: string +} + +export interface ApiResponse { + data: T + status: number + message?: string +} +``` + ## Important Notes - `allowJs: true` is enabled, so JavaScript and TypeScript can coexist - Existing JavaScript files will not show type errors - Type checking is strict for TypeScript files but permissive for JavaScript +- The `@/` path alias works in both JavaScript and TypeScript files diff --git a/frontend/app/src/components/TypeScriptTestComponent.vue b/frontend/app/src/components/TypeScriptTestComponent.vue deleted file mode 100644 index a306082bd..000000000 --- a/frontend/app/src/components/TypeScriptTestComponent.vue +++ /dev/null @@ -1,21 +0,0 @@ - - - diff --git a/frontend/app/src/types/index.ts b/frontend/app/src/types/index.ts new file mode 100644 index 000000000..666f2c692 --- /dev/null +++ b/frontend/app/src/types/index.ts @@ -0,0 +1,15 @@ +/** + * Common type definitions for the Endurain application + * + * This file serves as a starting point for TypeScript type definitions. + * As you migrate JavaScript code to TypeScript, add relevant types here. + */ + +// Example: API Response wrapper +export interface ApiResponse { + data: T + status: number + message?: string +} + +// Add your type definitions below as needed diff --git a/frontend/app/src/utils/typeTest.ts b/frontend/app/src/utils/typeTest.ts deleted file mode 100644 index 6c3d6d37e..000000000 --- a/frontend/app/src/utils/typeTest.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Simple test file to verify TypeScript support -export function greet(name: string): string { - return `Hello, ${name}!` -} - -export interface User { - id: number - name: string - email?: string -} - -export const createUser = (id: number, name: string, email?: string): User => { - return { id, name, email } -} From 3567cba8e043b236069f7f404699d9f5be4370f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vit=C3=B3ria=20Silva?= <8648976+joaovitoriasilva@users.noreply.github.com> Date: Thu, 2 Oct 2025 09:34:09 +0100 Subject: [PATCH 74/92] Update dependency lock files Updated backend/poetry.lock and frontend/app/package-lock.json to reflect new versions of dependencies. This ensures the project uses the latest compatible packages and resolves potential security or compatibility issues. --- backend/poetry.lock | 528 +++++++++++--------- frontend/app/package-lock.json | 878 +++++++++++++++++++-------------- 2 files changed, 815 insertions(+), 591 deletions(-) diff --git a/backend/poetry.lock b/backend/poetry.lock index 3cb422840..5316f90f5 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -34,14 +34,14 @@ files = [ [[package]] name = "anyio" -version = "4.10.0" +version = "4.11.0" description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1"}, - {file = "anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6"}, + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, ] [package.dependencies] @@ -50,18 +50,18 @@ sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -trio = ["trio (>=0.26.1)"] +trio = ["trio (>=0.31.0)"] [[package]] name = "apprise" -version = "1.9.4" +version = "1.9.5" description = "Push Notifications that work with just about every platform!" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "apprise-1.9.4-py3-none-any.whl", hash = "sha256:17dca8ad0a5b2063f6bae707979a51ca2cb374fcc66b0dd5c05a9d286dd40069"}, - {file = "apprise-1.9.4.tar.gz", hash = "sha256:483122aee19a89a7b075ecd48ef11ae37d79744f7aeb450bcf985a9a6c28c988"}, + {file = "apprise-1.9.5-py3-none-any.whl", hash = "sha256:1873a8a1b8cf9e44fcbefe0486ed260b590652aea12427f545b37c8566142961"}, + {file = "apprise-1.9.5.tar.gz", hash = "sha256:8f3be318bb429c2017470e33928a2e313cbf7600fc74b8184782a37060db366a"}, ] [package.dependencies] @@ -71,11 +71,12 @@ markdown = "*" PyYAML = "*" requests = "*" requests-oauthlib = "*" +tzdata = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] all-plugins = ["PGPy", "cryptography", "gntp", "paho-mqtt (!=2.0.*)", "smpplib"] dev = ["babel", "coverage", "mock", "pytest", "pytest-cov", "pytest-mock", "ruff", "tox", "validate-pyproject"] -windows = ["pywin32"] +windows = ["pywin32", "tzdata"] [[package]] name = "apscheduler" @@ -127,14 +128,14 @@ test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock [[package]] name = "asgiref" -version = "3.9.1" +version = "3.9.2" description = "ASGI specs, helper code, and adapters" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c"}, - {file = "asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142"}, + {file = "asgiref-3.9.2-py3-none-any.whl", hash = "sha256:0b61526596219d70396548fc003635056856dba5d0d086f86476f10b33c75960"}, + {file = "asgiref-3.9.2.tar.gz", hash = "sha256:a0249afacb66688ef258ffe503528360443e2b9a8d8c4581b6ebefa58c841ef1"}, ] [package.extras] @@ -447,14 +448,14 @@ rapidfuzz = ">=3.0.0,<4.0.0" [[package]] name = "click" -version = "8.2.1" +version = "8.3.0" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, - {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, + {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"}, + {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"}, ] [package.dependencies] @@ -677,52 +678,55 @@ wmi = ["wmi (>=1.5.1)"] [[package]] name = "dulwich" -version = "0.24.1" +version = "0.24.2" description = "Python Git Library" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dulwich-0.24.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2169c36b7955b40e1ec9b0543301a3dd536718c3b7840959ca70e7ed17397c25"}, - {file = "dulwich-0.24.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d16507ca6d6c2d29d7d942da4cc50fa589d58ab066030992dfa3932de6695062"}, - {file = "dulwich-0.24.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:e893b800c72499e21d0160169bac574292626193532c336ffce7617fe02d97db"}, - {file = "dulwich-0.24.1-cp310-cp310-win32.whl", hash = "sha256:d7144febcad9e8510ed870a141073b07071390421691285a81cea5b9fa38d888"}, - {file = "dulwich-0.24.1-cp310-cp310-win_amd64.whl", hash = "sha256:1d8226ca444c4347e5820b4a0a3a8f91753c0e39e335eee1eaf59d9672356a9c"}, - {file = "dulwich-0.24.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:31ad6637322aaafeecc4c884f396ac2d963aadf201deb6422134fa0f8ac9a87a"}, - {file = "dulwich-0.24.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:358e4b688f6c1fa5346a8394a2c1ab79ff7126be576b20ffd0f38085ead0df54"}, - {file = "dulwich-0.24.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:50f981edd5307475f6f862ccdbe39e8dd01afc17f2ed8ee0e452c3878389b48c"}, - {file = "dulwich-0.24.1-cp311-cp311-win32.whl", hash = "sha256:741417a6a029a3230c46ad4725a50440cac852f165706824303d9939cf83770c"}, - {file = "dulwich-0.24.1-cp311-cp311-win_amd64.whl", hash = "sha256:816ec4abd152ebd11d05bf25b5d37a4a88c18af59857067ee85d32e43af12b5f"}, - {file = "dulwich-0.24.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d227cebcb2082801ab429e196d973315dbe3818904b5c13a22d80a16f5692c9"}, - {file = "dulwich-0.24.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5e3c01b8109169aa361842af4987bca672087e3faf38d528ff9f631d1071f523"}, - {file = "dulwich-0.24.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ee80d8e9199124974b486d2c83a7e2d4db17ae59682909fa198111d8bb416f50"}, - {file = "dulwich-0.24.1-cp312-cp312-win32.whl", hash = "sha256:bef4dccba44edd6d18015f67c9e0d6216f978840cdbe703930e1679e2872c595"}, - {file = "dulwich-0.24.1-cp312-cp312-win_amd64.whl", hash = "sha256:83b2fb17bac190cfc6c91e7a94a1d806aa8ce8903aca0e3e54cecb2c3f547a55"}, - {file = "dulwich-0.24.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a11ec69fc6604228804ddfc32c85b22bc627eca4cf4ff3f27dbe822e6f29477"}, - {file = "dulwich-0.24.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a9800df7238b586b4c38c00432776781bc889cf02d756dcfb8dc0ecb8fc47a33"}, - {file = "dulwich-0.24.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3baab4a01aff890e2e6551ccbd33eb2a44173c897f0f027ad3aeab0fb057ec44"}, - {file = "dulwich-0.24.1-cp313-cp313-win32.whl", hash = "sha256:b39689aa4d143ba1fb0a687a4eb93d2e630d2c8f940aaa6c6911e9c8dca16e6a"}, - {file = "dulwich-0.24.1-cp313-cp313-win_amd64.whl", hash = "sha256:8fca9b863b939b52c5f759d292499f0d21a7bf7f8cbb9fdeb8cdd9511c5bc973"}, - {file = "dulwich-0.24.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62e3ed32e48e2a7e37c5a97071beac43040cd700d0cab7867514a91335916c83"}, - {file = "dulwich-0.24.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6d87cc4770a69d547ebbbac535c83af8a5b762d9e5b1c886c40cb457a1b5c2c1"}, - {file = "dulwich-0.24.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:10882685a8b03d0de321f277f6c8e6672bb849a378ad9823d482c42bb1ee8ee4"}, - {file = "dulwich-0.24.1-cp39-cp39-win32.whl", hash = "sha256:3df7ed03062e47f50675c83a7a1b73e48a95c9b413c2c8fca329b3e9f2700c04"}, - {file = "dulwich-0.24.1-cp39-cp39-win_amd64.whl", hash = "sha256:38000f553593e183189e06c6ed51377f106d28a2d98942d81eab9a10daef4663"}, - {file = "dulwich-0.24.1-py3-none-any.whl", hash = "sha256:57cc0dc5a21059698ffa4ed9a7272f1040ec48535193df84b0ee6b16bf615676"}, - {file = "dulwich-0.24.1.tar.gz", hash = "sha256:e19fd864f10f02bb834bb86167d92dcca1c228451b04458761fc13dabd447758"}, + {file = "dulwich-0.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca6ad94bcaa40153ce195eba4b1f5e58b8861f37c2cd7e017d640412ac3193df"}, + {file = "dulwich-0.24.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cd4910747b0ad91b2a0c211a67ba0d45a126b61b2cf7caf32d0eee0e48ab1543"}, + {file = "dulwich-0.24.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8e819f801382787164b6b0df0b9020a2d8fe0bdf4ed61e7208149a29fda31c39"}, + {file = "dulwich-0.24.2-cp310-cp310-win32.whl", hash = "sha256:d25911dbeaef3c3ee32a8e57f4a58fbd62abd644612ac733f8cab707fdd13a04"}, + {file = "dulwich-0.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:4f85fc7dd8aa4aa1c16e3dcb231270388a0d3c73be2214187a8ddb700300c7ea"}, + {file = "dulwich-0.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1551656144bf8e815f3ab8f15dfbce5399e8106ee43e4a48218ac41259289718"}, + {file = "dulwich-0.24.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:823d16592c4bbca3f089a09bb9e656254b6de1b41874309dce3b4df12e68b3fa"}, + {file = "dulwich-0.24.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:01ee9f1fcfabb6a58df9813b90856943cb1191bd1776c5c133ae03919d943fef"}, + {file = "dulwich-0.24.2-cp311-cp311-win32.whl", hash = "sha256:bc9314c49a44bfe6925e2619a021f339025d1e8a96ead966fa669dc0250fa254"}, + {file = "dulwich-0.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:64aab508c9bf9ec93659a15702ee5b046d2be6a1daa7096e6a94d7c887162c2e"}, + {file = "dulwich-0.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd1d9afdf511c167d30dd0f8fef2a0d5e548211644c8cef006e25a531fc0173d"}, + {file = "dulwich-0.24.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e918cdd05d03933334542eb7649066ff36f818ca90a9dc2027e1ea28192b74b4"}, + {file = "dulwich-0.24.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:be1cb36f6bf4b335f17b70cabedad3aa3d097364febeaa8531947781612c2a6b"}, + {file = "dulwich-0.24.2-cp312-cp312-win32.whl", hash = "sha256:fdb3fa61f3b98e6900aa57f73d2c4dd37822c66bb3e61ae3424f620c6353ee76"}, + {file = "dulwich-0.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:f7992e193d7cb5df1a043e41f0edfc9929c845f68cf7fdf09673d680ffa594ab"}, + {file = "dulwich-0.24.2-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:e237e3724d80be09e8e45f9f9a86342c9c0911ad3fd71fb66f17b20df47c0049"}, + {file = "dulwich-0.24.2-cp313-cp313-android_21_x86_64.whl", hash = "sha256:8e65c091aacf0d9da167e50f97e203947ae9e439ee8b020915b1decf9ecfdd6d"}, + {file = "dulwich-0.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:150b17b30a78c09f105b787ce74ccb094e9a27ada77d25a9df78758b858ee034"}, + {file = "dulwich-0.24.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:540928778a36d468cd98d6d6251af307e9f0743e824fe87189936ca0a88b10fb"}, + {file = "dulwich-0.24.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:fbc6c4ba4b54919d32ba516ea397008e9d1c4d774b87355c5e0fd320d3d86396"}, + {file = "dulwich-0.24.2-cp313-cp313-win32.whl", hash = "sha256:bc5452099ecb27970c17f8fede63f426509e7668361b9c7b0f495d498bf67c02"}, + {file = "dulwich-0.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:09c86b889a344667d050a8e0849816ed2f376d33ec9f8be302129a79ca65845b"}, + {file = "dulwich-0.24.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:316bf497761d349132470b3ad64200ebefd6c545386e35988267d548010b2ce0"}, + {file = "dulwich-0.24.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:67c888d65c8fbd4da3a60cc718cc543a261d3993cae9274e2f88e34517787a5b"}, + {file = "dulwich-0.24.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:2d92e1cfbd26bd172e7784be3cd8a32e804223d6655d22139cb0cb87d55c31b0"}, + {file = "dulwich-0.24.2-cp39-cp39-win32.whl", hash = "sha256:ea489a6c7f009b70d7efcac0a704f08808771f6ea29bddc46c1e85c106ddbb17"}, + {file = "dulwich-0.24.2-cp39-cp39-win_amd64.whl", hash = "sha256:7e150ad890e842af4659797b932edaf34ee5257414b818a648e013e07e2ca4a7"}, + {file = "dulwich-0.24.2-py3-none-any.whl", hash = "sha256:07426d209e870bd869d6eefb03573cf4879e62b30f690c53cc525b602ea413e5"}, + {file = "dulwich-0.24.2.tar.gz", hash = "sha256:d474844cf81bf95a6537a80aeec59d714d5d77d8e83d6d37991e2bde54746ca7"}, ] [package.dependencies] -urllib3 = ">=1.25" +urllib3 = ">=2.2.2" [package.extras] colordiff = ["rich"] -dev = ["dissolve (>=0.1.1)", "mypy (==1.17.0)", "ruff (==0.12.4)"] +dev = ["codespell (==2.4.1)", "dissolve (>=0.1.1)", "mypy (==1.17.1)", "ruff (==0.12.11)"] fastimport = ["fastimport"] fuzzing = ["atheris"] -https = ["urllib3 (>=1.24.1)"] +https = ["urllib3 (>=2.2.2)"] merge = ["merge3"] paramiko = ["paramiko"] +patiencediff = ["patiencediff"] pgp = ["gpg"] [[package]] @@ -822,14 +826,14 @@ docs = ["sphinx", "sphinx-rtd-theme"] [[package]] name = "flatbuffers" -version = "25.2.10" +version = "25.9.23" description = "The FlatBuffers serialization format for Python" optional = false python-versions = "*" groups = ["main"] files = [ - {file = "flatbuffers-25.2.10-py2.py3-none-any.whl", hash = "sha256:ebba5f4d5ea615af3f7fd70fc310636fbb2bbd1f566ac0a23d98dd412de50051"}, - {file = "flatbuffers-25.2.10.tar.gz", hash = "sha256:97e451377a41262f8d9bd4295cc836133415cc03d8cb966410a4af92eb00d26e"}, + {file = "flatbuffers-25.9.23-py2.py3-none-any.whl", hash = "sha256:255538574d6cb6d0a79a17ec8bc0d30985913b87513a01cce8bcdb6b4c44d0e2"}, + {file = "flatbuffers-25.9.23.tar.gz", hash = "sha256:676f9fa62750bb50cf531b42a0a2a118ad8f7f797a511eda12881c016f093b12"}, ] [[package]] @@ -1043,70 +1047,80 @@ test = ["objgraph", "psutil", "setuptools"] [[package]] name = "grpcio" -version = "1.75.0" +version = "1.75.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "grpcio-1.75.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:1ec9cbaec18d9597c718b1ed452e61748ac0b36ba350d558f9ded1a94cc15ec7"}, - {file = "grpcio-1.75.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:7ee5ee42bfae8238b66a275f9ebcf6f295724375f2fa6f3b52188008b6380faf"}, - {file = "grpcio-1.75.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9146e40378f551eed66c887332afc807fcce593c43c698e21266a4227d4e20d2"}, - {file = "grpcio-1.75.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0c40f368541945bb664857ecd7400acb901053a1abbcf9f7896361b2cfa66798"}, - {file = "grpcio-1.75.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:50a6e43a9adc6938e2a16c9d9f8a2da9dd557ddd9284b73b07bd03d0e098d1e9"}, - {file = "grpcio-1.75.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dce15597ca11913b78e1203c042d5723e3ea7f59e7095a1abd0621be0e05b895"}, - {file = "grpcio-1.75.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:851194eec47755101962da423f575ea223c9dd7f487828fe5693920e8745227e"}, - {file = "grpcio-1.75.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ca123db0813eef80625a4242a0c37563cb30a3edddebe5ee65373854cf187215"}, - {file = "grpcio-1.75.0-cp310-cp310-win32.whl", hash = "sha256:222b0851e20c04900c63f60153503e918b08a5a0fad8198401c0b1be13c6815b"}, - {file = "grpcio-1.75.0-cp310-cp310-win_amd64.whl", hash = "sha256:bb58e38a50baed9b21492c4b3f3263462e4e37270b7ea152fc10124b4bd1c318"}, - {file = "grpcio-1.75.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:7f89d6d0cd43170a80ebb4605cad54c7d462d21dc054f47688912e8bf08164af"}, - {file = "grpcio-1.75.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:cb6c5b075c2d092f81138646a755f0dad94e4622300ebef089f94e6308155d82"}, - {file = "grpcio-1.75.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:494dcbade5606128cb9f530ce00331a90ecf5e7c5b243d373aebdb18e503c346"}, - {file = "grpcio-1.75.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:050760fd29c8508844a720f06c5827bb00de8f5e02f58587eb21a4444ad706e5"}, - {file = "grpcio-1.75.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:266fa6209b68a537b2728bb2552f970e7e78c77fe43c6e9cbbe1f476e9e5c35f"}, - {file = "grpcio-1.75.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:06d22e1d8645e37bc110f4c589cb22c283fd3de76523065f821d6e81de33f5d4"}, - {file = "grpcio-1.75.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9880c323595d851292785966cadb6c708100b34b163cab114e3933f5773cba2d"}, - {file = "grpcio-1.75.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:55a2d5ae79cd0f68783fb6ec95509be23746e3c239290b2ee69c69a38daa961a"}, - {file = "grpcio-1.75.0-cp311-cp311-win32.whl", hash = "sha256:352dbdf25495eef584c8de809db280582093bc3961d95a9d78f0dfb7274023a2"}, - {file = "grpcio-1.75.0-cp311-cp311-win_amd64.whl", hash = "sha256:678b649171f229fb16bda1a2473e820330aa3002500c4f9fd3a74b786578e90f"}, - {file = "grpcio-1.75.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fa35ccd9501ffdd82b861809cbfc4b5b13f4b4c5dc3434d2d9170b9ed38a9054"}, - {file = "grpcio-1.75.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:0fcb77f2d718c1e58cc04ef6d3b51e0fa3b26cf926446e86c7eba105727b6cd4"}, - {file = "grpcio-1.75.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36764a4ad9dc1eb891042fab51e8cdf7cc014ad82cee807c10796fb708455041"}, - {file = "grpcio-1.75.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:725e67c010f63ef17fc052b261004942763c0b18dcd84841e6578ddacf1f9d10"}, - {file = "grpcio-1.75.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91fbfc43f605c5ee015c9056d580a70dd35df78a7bad97e05426795ceacdb59f"}, - {file = "grpcio-1.75.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a9337ac4ce61c388e02019d27fa837496c4b7837cbbcec71b05934337e51531"}, - {file = "grpcio-1.75.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ee16e232e3d0974750ab5f4da0ab92b59d6473872690b5e40dcec9a22927f22e"}, - {file = "grpcio-1.75.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55dfb9122973cc69520b23d39867726722cafb32e541435707dc10249a1bdbc6"}, - {file = "grpcio-1.75.0-cp312-cp312-win32.whl", hash = "sha256:fb64dd62face3d687a7b56cd881e2ea39417af80f75e8b36f0f81dfd93071651"}, - {file = "grpcio-1.75.0-cp312-cp312-win_amd64.whl", hash = "sha256:6b365f37a9c9543a9e91c6b4103d68d38d5bcb9965b11d5092b3c157bd6a5ee7"}, - {file = "grpcio-1.75.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:1bb78d052948d8272c820bb928753f16a614bb2c42fbf56ad56636991b427518"}, - {file = "grpcio-1.75.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:9dc4a02796394dd04de0b9673cb79a78901b90bb16bf99ed8cb528c61ed9372e"}, - {file = "grpcio-1.75.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:437eeb16091d31498585d73b133b825dc80a8db43311e332c08facf820d36894"}, - {file = "grpcio-1.75.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c2c39984e846bd5da45c5f7bcea8fafbe47c98e1ff2b6f40e57921b0c23a52d0"}, - {file = "grpcio-1.75.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38d665f44b980acdbb2f0e1abf67605ba1899f4d2443908df9ec8a6f26d2ed88"}, - {file = "grpcio-1.75.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e8e752ab5cc0a9c5b949808c000ca7586223be4f877b729f034b912364c3964"}, - {file = "grpcio-1.75.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3a6788b30aa8e6f207c417874effe3f79c2aa154e91e78e477c4825e8b431ce0"}, - {file = "grpcio-1.75.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc33e67cab6141c54e75d85acd5dec616c5095a957ff997b4330a6395aa9b51"}, - {file = "grpcio-1.75.0-cp313-cp313-win32.whl", hash = "sha256:c8cfc780b7a15e06253aae5f228e1e84c0d3c4daa90faf5bc26b751174da4bf9"}, - {file = "grpcio-1.75.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c91d5b16eff3cbbe76b7a1eaaf3d91e7a954501e9d4f915554f87c470475c3d"}, - {file = "grpcio-1.75.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:0b85f4ebe6b56d2a512201bb0e5f192c273850d349b0a74ac889ab5d38959d16"}, - {file = "grpcio-1.75.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:68c95b1c1e3bf96ceadf98226e9dfe2bc92155ce352fa0ee32a1603040e61856"}, - {file = "grpcio-1.75.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:153c5a7655022c3626ad70be3d4c2974cb0967f3670ee49ece8b45b7a139665f"}, - {file = "grpcio-1.75.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:53067c590ac3638ad0c04272f2a5e7e32a99fec8824c31b73bc3ef93160511fa"}, - {file = "grpcio-1.75.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:78dcc025a144319b66df6d088bd0eda69e1719eb6ac6127884a36188f336df19"}, - {file = "grpcio-1.75.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ec2937fd92b5b4598cbe65f7e57d66039f82b9e2b7f7a5f9149374057dde77d"}, - {file = "grpcio-1.75.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:597340a41ad4b619aaa5c9b94f7e6ba4067885386342ab0af039eda945c255cd"}, - {file = "grpcio-1.75.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0aa795198b28807d28570c0a5f07bb04d5facca7d3f27affa6ae247bbd7f312a"}, - {file = "grpcio-1.75.0-cp39-cp39-win32.whl", hash = "sha256:585147859ff4603798e92605db28f4a97c821c69908e7754c44771c27b239bbd"}, - {file = "grpcio-1.75.0-cp39-cp39-win_amd64.whl", hash = "sha256:eafbe3563f9cb378370a3fa87ef4870539cf158124721f3abee9f11cd8162460"}, - {file = "grpcio-1.75.0.tar.gz", hash = "sha256:b989e8b09489478c2d19fecc744a298930f40d8b27c3638afbfe84d22f36ce4e"}, + {file = "grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088"}, + {file = "grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b"}, + {file = "grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9"}, + {file = "grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61"}, + {file = "grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326"}, + {file = "grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca"}, + {file = "grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe"}, + {file = "grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772"}, + {file = "grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018"}, + {file = "grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de"}, + {file = "grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945"}, + {file = "grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d"}, + {file = "grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884"}, + {file = "grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc"}, + {file = "grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970"}, + {file = "grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66"}, + {file = "grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7"}, + {file = "grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0"}, + {file = "grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c"}, + {file = "grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464"}, + {file = "grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb"}, + {file = "grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939"}, + {file = "grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41"}, + {file = "grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383"}, + {file = "grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2"}, ] [package.dependencies] typing-extensions = ">=4.12,<5.0" [package.extras] -protobuf = ["grpcio-tools (>=1.75.0)"] +protobuf = ["grpcio-tools (>=1.75.1)"] [[package]] name = "h11" @@ -1358,14 +1372,14 @@ trio = ["trio"] [[package]] name = "joserfc" -version = "1.3.3" +version = "1.3.4" description = "The ultimate Python library for JOSE RFCs, including JWS, JWE, JWK, JWA, JWT" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "joserfc-1.3.3-py3-none-any.whl", hash = "sha256:3ff23bf4dd8564b07d4844078cc376064687c6991ebcdcb7822bb90c0f2eb073"}, - {file = "joserfc-1.3.3.tar.gz", hash = "sha256:f9a80eb9f179a6c594d48cb2c29c5147d308dd7d8e353da2a14b57e16c7178f5"}, + {file = "joserfc-1.3.4-py3-none-any.whl", hash = "sha256:30c845c58d441cfe32d08ac35e437812481ca8155373873b7abf80224bf601c0"}, + {file = "joserfc-1.3.4.tar.gz", hash = "sha256:67d8413c501c239f65eefad5ae685cfbfc401aa63289fc409ef7cc331b007227"}, ] [package.dependencies] @@ -1441,73 +1455,101 @@ testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" -version = "3.0.2" +version = "3.0.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, - {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, ] [[package]] @@ -1935,14 +1977,14 @@ files = [ [[package]] name = "pbs-installer" -version = "2025.9.2" +version = "2025.9.18" description = "Installer for Python Build Standalone" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "pbs_installer-2025.9.2-py3-none-any.whl", hash = "sha256:659a5399278c810761c1e7bc54095f38af11a5b593ce8d45c41a3a9d6759d8f1"}, - {file = "pbs_installer-2025.9.2.tar.gz", hash = "sha256:0da1d59bb5c4d8cfb5aee29ac2a37b37d651a45ab5ede19d1331df9a92464b5d"}, + {file = "pbs_installer-2025.9.18-py3-none-any.whl", hash = "sha256:8ef55d7675698747505c237015d14c81759bd66a0d4c8b20cec9a2dc96e8434c"}, + {file = "pbs_installer-2025.9.18.tar.gz", hash = "sha256:c0a51a7c1e015723bd8396f02e15b5876e439f74b0f45bbac436b189f903219f"}, ] [package.dependencies] @@ -2147,14 +2189,14 @@ type = ["mypy (>=1.14.1)"] [[package]] name = "poetry" -version = "2.2.0" +version = "2.2.1" description = "Python dependency management and packaging made easy." optional = false python-versions = "<4.0,>=3.9" groups = ["main"] files = [ - {file = "poetry-2.2.0-py3-none-any.whl", hash = "sha256:1eb2dde482c0fee65c3b5be85a2cd0ad7c8be05c42041d8555ab89436c433c5f"}, - {file = "poetry-2.2.0.tar.gz", hash = "sha256:c6bc7e9d2d5aad4f6818cc5eef1f85fcfb7ee49a1aab3b4ff66d0c6874e74769"}, + {file = "poetry-2.2.1-py3-none-any.whl", hash = "sha256:f5958b908b96c5824e2acbb8b19cdef8a3351c62142d7ecff2d705396c8ca34c"}, + {file = "poetry-2.2.1.tar.gz", hash = "sha256:bef9aa4bb00ce4c10b28b25e7bac724094802d6958190762c45df6c12749b37c"}, ] [package.dependencies] @@ -2170,7 +2212,7 @@ packaging = ">=24.2" pbs-installer = {version = ">=2025.1.6,<2026.0.0", extras = ["download", "install"]} pkginfo = ">=1.12,<2.0" platformdirs = ">=3.0.0,<5" -poetry-core = "2.2.0" +poetry-core = "2.2.1" pyproject-hooks = ">=1.0.0,<2.0.0" requests = ">=2.26,<3.0" requests-toolbelt = ">=1.0.0,<2.0.0" @@ -2182,14 +2224,14 @@ xattr = {version = ">=1.0.0,<2.0.0", markers = "sys_platform == \"darwin\""} [[package]] name = "poetry-core" -version = "2.2.0" +version = "2.2.1" description = "Poetry PEP 517 Build Backend" optional = false python-versions = "<4.0,>=3.9" groups = ["main"] files = [ - {file = "poetry_core-2.2.0-py3-none-any.whl", hash = "sha256:0edea81d07e88cbd407369eef753c722da8ff1338f554788dc04636e756318fc"}, - {file = "poetry_core-2.2.0.tar.gz", hash = "sha256:b4033b71b99717a942030e074fec7e3082e5fde7a8ed10f02cd2413bdf940b1f"}, + {file = "poetry_core-2.2.1-py3-none-any.whl", hash = "sha256:bdfce710edc10bfcf9ab35041605c480829be4ab23f5bc01202cfe5db8f125ab"}, + {file = "poetry_core-2.2.1.tar.gz", hash = "sha256:97e50d8593c8729d3f49364b428583e044087ee3def1e010c6496db76bd65ac5"}, ] [[package]] @@ -2570,65 +2612,85 @@ files = [ [[package]] name = "pyyaml" -version = "6.0.2" +version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, ] [[package]] @@ -3094,14 +3156,14 @@ files = [ [[package]] name = "typing-inspection" -version = "0.4.1" +version = "0.4.2" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, - {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, ] [package.dependencies] diff --git a/frontend/app/package-lock.json b/frontend/app/package-lock.json index 6a2157334..0fccb130a 100644 --- a/frontend/app/package-lock.json +++ b/frontend/app/package-lock.json @@ -2227,19 +2227,6 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", @@ -2265,6 +2252,30 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/config-helpers": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", @@ -2312,10 +2323,44 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { - "version": "9.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", - "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", "dev": true, "license": "MIT", "engines": { @@ -2753,10 +2798,23 @@ "dev": true, "license": "MIT" }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", - "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", "cpu": [ "arm" ], @@ -2768,9 +2826,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", - "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", "cpu": [ "arm64" ], @@ -2782,9 +2840,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.2.tgz", - "integrity": "sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", "cpu": [ "arm64" ], @@ -2796,9 +2854,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", - "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", "cpu": [ "x64" ], @@ -2810,9 +2868,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", - "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", "cpu": [ "arm64" ], @@ -2824,9 +2882,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", - "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", "cpu": [ "x64" ], @@ -2838,9 +2896,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", - "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", "cpu": [ "arm" ], @@ -2852,9 +2910,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", - "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", "cpu": [ "arm" ], @@ -2866,9 +2924,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.2.tgz", - "integrity": "sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", "cpu": [ "arm64" ], @@ -2880,9 +2938,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.2.tgz", - "integrity": "sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", "cpu": [ "arm64" ], @@ -2894,9 +2952,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.50.2.tgz", - "integrity": "sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", "cpu": [ "loong64" ], @@ -2908,9 +2966,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.2.tgz", - "integrity": "sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", "cpu": [ "ppc64" ], @@ -2922,9 +2980,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.2.tgz", - "integrity": "sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", "cpu": [ "riscv64" ], @@ -2936,9 +2994,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.2.tgz", - "integrity": "sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", "cpu": [ "riscv64" ], @@ -2950,9 +3008,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.2.tgz", - "integrity": "sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", "cpu": [ "s390x" ], @@ -2964,9 +3022,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.2.tgz", - "integrity": "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", "cpu": [ "x64" ], @@ -2978,9 +3036,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.2.tgz", - "integrity": "sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", "cpu": [ "x64" ], @@ -2992,9 +3050,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.2.tgz", - "integrity": "sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", "cpu": [ "arm64" ], @@ -3006,9 +3064,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.2.tgz", - "integrity": "sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", "cpu": [ "arm64" ], @@ -3020,9 +3078,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.2.tgz", - "integrity": "sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", "cpu": [ "ia32" ], @@ -3033,10 +3091,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.2.tgz", - "integrity": "sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", "cpu": [ "x64" ], @@ -3148,9 +3220,9 @@ } }, "node_modules/@types/node": { - "version": "24.6.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.1.tgz", - "integrity": "sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw==", + "version": "24.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz", + "integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==", "dev": true, "license": "MIT", "dependencies": { @@ -3208,16 +3280,6 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/@typescript-eslint/parser": { "version": "8.45.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", @@ -3368,32 +3430,6 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/utils": { "version": "8.45.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", @@ -3436,6 +3472,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@vitejs/plugin-vue": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", @@ -3595,13 +3644,13 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", - "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz", + "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@vue/shared": "3.5.21", + "@babel/parser": "^7.28.4", + "@vue/shared": "3.5.22", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -3626,28 +3675,28 @@ "license": "MIT" }, "node_modules/@vue/compiler-dom": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", - "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz", + "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/compiler-core": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", - "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz", + "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@vue/compiler-core": "3.5.21", - "@vue/compiler-dom": "3.5.21", - "@vue/compiler-ssr": "3.5.21", - "@vue/shared": "3.5.21", + "@babel/parser": "^7.28.4", + "@vue/compiler-core": "3.5.22", + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22", "estree-walker": "^2.0.2", - "magic-string": "^0.30.18", + "magic-string": "^0.30.19", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } @@ -3659,13 +3708,13 @@ "license": "MIT" }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", - "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz", + "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/compiler-dom": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/devtools-api": { @@ -3766,54 +3815,67 @@ } } }, + "node_modules/@vue/language-core/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@vue/reactivity": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz", - "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.22.tgz", + "integrity": "sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.21" + "@vue/shared": "3.5.22" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz", - "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.22.tgz", + "integrity": "sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/reactivity": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz", - "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.22.tgz", + "integrity": "sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.21", - "@vue/runtime-core": "3.5.21", - "@vue/shared": "3.5.21", + "@vue/reactivity": "3.5.22", + "@vue/runtime-core": "3.5.22", + "@vue/shared": "3.5.22", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz", - "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.22.tgz", + "integrity": "sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22" }, "peerDependencies": { - "vue": "3.5.21" + "vue": "3.5.22" } }, "node_modules/@vue/shared": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", - "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz", + "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==", "license": "MIT" }, "node_modules/@vue/test-utils": { @@ -4101,9 +4163,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.5.tgz", - "integrity": "sha512-TiU4qUT9jdCuh4aVOG7H1QozyeI2sZRqoRPdqBIaslfNt4WUSanRBueAwl2x5jt4rXBMim3lIN2x6yT8PDi24Q==", + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz", + "integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4111,9 +4173,9 @@ } }, "node_modules/birpc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz", - "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.6.1.tgz", + "integrity": "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" @@ -4146,14 +4208,13 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -4170,9 +4231,9 @@ } }, "node_modules/browserslist": { - "version": "4.26.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", - "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", "dev": true, "funding": [ { @@ -4190,9 +4251,9 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.3", - "caniuse-lite": "^1.0.30001741", - "electron-to-chromium": "^1.5.218", + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, @@ -4281,9 +4342,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001743", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", - "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "version": "1.0.30001746", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz", + "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==", "dev": true, "funding": [ { @@ -4715,16 +4776,6 @@ "node": ">=14" } }, - "node_modules/editorconfig/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/editorconfig/node_modules/minimatch": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", @@ -4758,9 +4809,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.221", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.221.tgz", - "integrity": "sha512-/1hFJ39wkW01ogqSyYoA4goOXOtMRy6B+yvA1u42nnsEGtHzIzmk93aPISumVQeblj47JUHLC9coCjUxb1EvtQ==", + "version": "1.5.228", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz", + "integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==", "dev": true, "license": "ISC" }, @@ -4993,9 +5044,9 @@ } }, "node_modules/eslint": { - "version": "9.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", - "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5005,7 +5056,7 @@ "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.35.0", + "@eslint/js": "9.36.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -5101,9 +5152,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.4.0.tgz", - "integrity": "sha512-K6tP0dW8FJVZLQxa2S7LcE1lLw3X8VvB3t887Q6CLrFVxHYBXGANbXvwNzYIu6Ughx1bSJ5BDT0YB3ybPT39lw==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.5.0.tgz", + "integrity": "sha512-7BZHsG3kC2vei8F2W8hnfDi9RK+cv5eKPMvzBdrl8Vuc0hR5odGQRli8VVzUkrmUHkxFEm4Iio1r5HOKslO0Aw==", "dev": true, "license": "MIT", "dependencies": { @@ -5118,11 +5169,15 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { + "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", "vue-eslint-parser": "^10.0.0" }, "peerDependenciesMeta": { + "@stylistic/eslint-plugin": { + "optional": true + }, "@typescript-eslint/parser": { "optional": true } @@ -5146,6 +5201,30 @@ } }, "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", @@ -5158,6 +5237,29 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -5176,6 +5278,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -5327,24 +5442,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5368,16 +5465,6 @@ "minimatch": "^5.0.1" } }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/filelist/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -5560,6 +5647,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5668,32 +5765,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -5913,9 +5984,9 @@ "license": "ISC" }, "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -6162,14 +6233,15 @@ } }, "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" }, @@ -6846,30 +6918,20 @@ "node": ">=8.6" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minipass": { @@ -7032,6 +7094,19 @@ "node": ">=16" } }, + "node_modules/npm-run-all2/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/npm-run-all2/node_modules/which": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", @@ -7314,13 +7389,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -7592,16 +7667,16 @@ } }, "node_modules/regexpu-core": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.3.1.tgz", - "integrity": "sha512-DzcswPr252wEr7Qz8AyAVbfyBDKLoYp6eRA1We2Fa9qirRFSdtkP5sHr3yglDKy2BbA0fd2T+j/CUSKes3FeVQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", + "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" }, @@ -7617,31 +7692,18 @@ "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", + "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "jsesc": "~3.0.2" + "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -7701,9 +7763,9 @@ "license": "MIT" }, "node_modules/rollup": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.2.tgz", - "integrity": "sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", "dev": true, "license": "MIT", "dependencies": { @@ -7717,27 +7779,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.50.2", - "@rollup/rollup-android-arm64": "4.50.2", - "@rollup/rollup-darwin-arm64": "4.50.2", - "@rollup/rollup-darwin-x64": "4.50.2", - "@rollup/rollup-freebsd-arm64": "4.50.2", - "@rollup/rollup-freebsd-x64": "4.50.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.50.2", - "@rollup/rollup-linux-arm-musleabihf": "4.50.2", - "@rollup/rollup-linux-arm64-gnu": "4.50.2", - "@rollup/rollup-linux-arm64-musl": "4.50.2", - "@rollup/rollup-linux-loong64-gnu": "4.50.2", - "@rollup/rollup-linux-ppc64-gnu": "4.50.2", - "@rollup/rollup-linux-riscv64-gnu": "4.50.2", - "@rollup/rollup-linux-riscv64-musl": "4.50.2", - "@rollup/rollup-linux-s390x-gnu": "4.50.2", - "@rollup/rollup-linux-x64-gnu": "4.50.2", - "@rollup/rollup-linux-x64-musl": "4.50.2", - "@rollup/rollup-openharmony-arm64": "4.50.2", - "@rollup/rollup-win32-arm64-msvc": "4.50.2", - "@rollup/rollup-win32-ia32-msvc": "4.50.2", - "@rollup/rollup-win32-x64-msvc": "4.50.2", + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", "fsevents": "~2.3.2" } }, @@ -8427,9 +8490,9 @@ } }, "node_modules/strip-literal": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", - "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", "dev": true, "license": "MIT", "dependencies": { @@ -8586,6 +8649,37 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", @@ -8607,9 +8701,9 @@ } }, "node_modules/tinyspy": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", - "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", "dev": true, "license": "MIT", "engines": { @@ -9111,6 +9205,37 @@ } } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vitest": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", @@ -9184,6 +9309,19 @@ } } }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vscode-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", @@ -9192,16 +9330,16 @@ "license": "MIT" }, "node_modules/vue": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", - "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz", + "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.21", - "@vue/compiler-sfc": "3.5.21", - "@vue/runtime-dom": "3.5.21", - "@vue/server-renderer": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-sfc": "3.5.22", + "@vue/runtime-dom": "3.5.22", + "@vue/server-renderer": "3.5.22", + "@vue/shared": "3.5.22" }, "peerDependencies": { "typescript": "*" @@ -9243,6 +9381,19 @@ "eslint": "^8.57.0 || ^9.0.0" } }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/vue-i18n": { "version": "11.1.12", "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.1.12.tgz", @@ -9677,6 +9828,17 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/workbox-build/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/workbox-build/node_modules/estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", @@ -9723,17 +9885,17 @@ "sourcemap-codec": "^1.4.8" } }, - "node_modules/workbox-build/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/workbox-build/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": "*" } }, "node_modules/workbox-build/node_modules/pretty-bytes": { From aabb205ae3665e82b12223d77fb1d1ed0c3ef442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vit=C3=B3ria=20Silva?= <8648976+joaovitoriasilva@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:06:12 +0100 Subject: [PATCH 75/92] Add print_to_console utility for console logging Introduces the print_to_console function to print messages to the console at various log levels without writing to log files. This provides a simple way to output log messages directly to the console, complementing existing logging utilities. --- backend/app/core/logger.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/backend/app/core/logger.py b/backend/app/core/logger.py index dbe0eae7f..b5bfe29f5 100644 --- a/backend/app/core/logger.py +++ b/backend/app/core/logger.py @@ -2,6 +2,7 @@ import logging import core.config as core_config + def setup_main_logger(): """ Sets up the main application logger and attaches a file handler to it, as well as to the Alembic and APScheduler loggers. @@ -53,7 +54,9 @@ def get_main_logger(): return logging.getLogger("main_logger") -def print_to_log(message: str, log_level: str = "info", exc: Exception = None, context = None): +def print_to_log( + message: str, log_level: str = "info", exc: Exception = None, context=None +): """ Logs a message at the specified log level using the main logger. @@ -76,6 +79,24 @@ def print_to_log(message: str, log_level: str = "info", exc: Exception = None, c main_logger.debug(message) +def print_to_console(message: str, log_level: str = "info"): + """ + Prints a message to the console only (without logging to file). + + Args: + message (str): The message to print. + log_level (str, optional): The log level to display ('info', 'error', 'warning', 'debug'). Defaults to "info". + """ + if log_level == "info": + print(f"INFO: {message}") + elif log_level == "error": + print(f"ERROR: {message}") + elif log_level == "warning": + print(f"WARNING: {message}") + elif log_level == "debug": + print(f"DEBUG: {message}") + + def print_to_log_and_console( message: str, log_level: str = "info", exc: Exception = None ): From 9faf22d42a6804a7f65f45299f26bc38c73c17e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vit=C3=B3ria=20Silva?= <8648976+joaovitoriasilva@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:13:23 +0100 Subject: [PATCH 76/92] Update Crowdin config and remove TypeScript guide Changed translation path variables in crowdin.yml to use %original_file_name% for consistency. Removed the frontend/app/TYPESCRIPT.md documentation file, likely to consolidate or relocate TypeScript support information. --- crowdin.yml | 8 +- frontend/app/TYPESCRIPT.md | 175 ------------------------------------- 2 files changed, 4 insertions(+), 179 deletions(-) delete mode 100644 frontend/app/TYPESCRIPT.md diff --git a/crowdin.yml b/crowdin.yml index 0d6b99856..053d7704b 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,12 +1,12 @@ files: - source: /frontend/app/src/i18n/us/*.json - translation: /frontend/app/src/i18n/%two_letters_code%/%file_name%.%file_extension% + translation: /frontend/app/src/i18n/%two_letters_code%/%original_file_name% - source: /frontend/app/src/i18n/us/components/*.json - translation: /frontend/app/src/i18n/%two_letters_code%/components/%file_name%.%file_extension% + translation: /frontend/app/src/i18n/%two_letters_code%/components/%original_file_name% - source: /frontend/app/src/i18n/us/gears/*.json - translation: /frontend/app/src/i18n/%two_letters_code%/gears/%file_name%.%file_extension% + translation: /frontend/app/src/i18n/%two_letters_code%/gears/%original_file_name% - source: /frontend/app/src/i18n/us/strava/*.json - translation: /frontend/app/src/i18n/%two_letters_code%/strava/%file_name%.%file_extension% + translation: /frontend/app/src/i18n/%two_letters_code%/strava/%original_file_name% - source: /frontend/app/src/i18n/us/components/activities/*.json translation: /frontend/app/src/i18n/%two_letters_code%/components/activities/%original_file_name% - source: /frontend/app/src/i18n/us/components/activities/modals/*.json diff --git a/frontend/app/TYPESCRIPT.md b/frontend/app/TYPESCRIPT.md deleted file mode 100644 index 54c25562d..000000000 --- a/frontend/app/TYPESCRIPT.md +++ /dev/null @@ -1,175 +0,0 @@ -# TypeScript Support - -This project now supports TypeScript alongside JavaScript, enabling gradual migration and improved developer experience. - -## Overview - -- **Existing JavaScript files (.js, .vue)** continue to work without any modifications -- **New files** can be written in TypeScript (.ts) or Vue with TypeScript (` -``` - -Or use the Composition API with ` -``` - -### Type Checking - -Run type checking manually: - -```bash -npm run type-check -``` - -The build process (`npm run build`) now includes type checking automatically. - -### Linting - -TypeScript files are configured to work with ESLint: - -```bash -npm run lint -``` - -Note: The project uses ESLint 9 which requires migration from `.eslintrc.cjs` to `eslint.config.js`. The current ESLint configuration may need updates for full functionality. - -### IDE Support - -TypeScript configuration is provided via `tsconfig.json`, which enables: -- IntelliSense and autocompletion -- Type checking in your IDE -- Better refactoring support -- Import path resolution for `@/` alias - -## Configuration Files - -- `tsconfig.json` - Root configuration with project references -- `tsconfig.app.json` - Configuration for application code -- `tsconfig.node.json` - Configuration for Node.js build tools (Vite, Vitest) -- `tsconfig.vitest.json` - Configuration for test files -- `src/env.d.ts` - Vite and global type declarations - -## Migration Strategy - -TypeScript is **optional** and migration can be done incrementally: - -1. Start with new features - write new components/utilities in TypeScript -2. Gradually migrate existing files when making significant changes -3. No immediate action required for existing JavaScript files - -## Practical Examples - -### Migrating a JavaScript Utility to TypeScript - -Before (JavaScript): -```javascript -// src/utils/example.js -export function calculateDistance(lat1, lon1, lat2, lon2) { - // ... implementation - return distance -} -``` - -After (TypeScript): -```typescript -// src/utils/example.ts -export function calculateDistance( - lat1: number, - lon1: number, - lat2: number, - lon2: number -): number { - // ... implementation - return distance -} -``` - -### Using Existing JavaScript Files from TypeScript - -TypeScript files can import and use existing JavaScript modules: - -```typescript -// Import from existing JS service -import { fetchUserProfile } from '@/services/profileService' - -// TypeScript provides type safety for your new code -const profile = await fetchUserProfile(userId) -``` - -### Defining Types for API Responses - -```typescript -// src/types/api.ts -export interface Activity { - id: number - name: string - distance: number - duration: number - date: string -} - -export interface ApiResponse { - data: T - status: number - message?: string -} -``` - -## Important Notes - -- `allowJs: true` is enabled, so JavaScript and TypeScript can coexist -- Existing JavaScript files will not show type errors -- Type checking is strict for TypeScript files but permissive for JavaScript -- The `@/` path alias works in both JavaScript and TypeScript files From 362cf80ef4302830d8dc785e9818884b7beb6af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vit=C3=B3ria=20Silva?= <8648976+joaovitoriasilva@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:38:59 +0100 Subject: [PATCH 77/92] Add Chinese language support (zh-CN, zh-TW) Added Simplified and Traditional Chinese language options to backend and frontend language switchers, user profile, and user modal. Included zh-CN and zh-TW translation files and updated i18n configuration and English labels for new languages. --- backend/app/users/user/schema.py | 4 + .../NavbarLanguageSwitcherComponent.vue | 10 ++- .../SettingsLanguageSwitcherComponent.vue | 2 + .../Settings/SettingsUserProfileZone.vue | 6 ++ .../UsersAddEditUserModalComponent.vue | 2 + frontend/app/src/i18n/index.js | 6 +- frontend/app/src/i18n/us/generalItems.json | 2 + frontend/app/src/i18n/us/signupView.json | 2 + .../app/src/i18n/zh-CN/activitiesView.json | 14 +++ .../app/src/i18n/zh-CN/activityItems.json | 42 +++++++++ frontend/app/src/i18n/zh-CN/activityView.json | 13 +++ .../activities/activitiesTableComponent.json | 12 +++ .../activityBellowMPillsComponent.json | 25 ++++++ .../activities/activityLapsComponent.json | 14 +++ .../activityMandAbovePillsComponent.json | 17 ++++ .../activities/activityMapComponent.json | 8 ++ .../activities/activitySummaryComponent.json | 31 +++++++ .../activityWorkoutStepsComponent.json | 20 +++++ .../addGearToActivityModalComponent.json | 6 ++ .../modals/editActivityModalComponent.json | 68 ++++++++++++++ .../followers/followersListComponent.json | 16 ++++ .../gearComponentAddEditModalComponent.json | 19 ++++ .../gears/gearComponentListComponent.json | 79 ++++++++++++++++ .../gears/gearsAddEditGearModalComponent.json | 29 ++++++ .../components/gears/gearsListComponent.json | 16 ++++ .../health/healthDashboardZoneComponent.json | 13 +++ .../health/healthSideBarComponent.json | 4 + .../healthWeightAddEditModalComponent.json | 8 ++ .../healthWeightListComponent.json | 9 ++ .../health/healthWeightZoneComponent.json | 6 ++ .../navbar/navbarBottomMobileComponent.json | 7 ++ .../components/navbar/navbarComponent.json | 13 +++ .../components/noItemsFoundComponent.json | 4 + ...pApprovalRequestNotificationComponent.json | 4 + .../navbarNotificationsComponent.json | 6 ++ ...wAcceptedRequestNotificationComponent.json | 4 + ...plicateStartTimeNotificationComponent.json | 4 + .../newActivityNotificationComponent.json | 4 + ...wFollowerRequestNotificationComponent.json | 4 + .../settingsLanguageSwitcherComponent.json | 3 + .../settingsThemeSwitcherComponent.json | 6 ++ .../settings/settingsImportZoneComponent.json | 13 +++ .../garminConnectLoginModalComponent.json | 14 +++ .../settingsIntegrationsZoneComponent.json | 47 ++++++++++ .../settingsSecurityZoneComponent.json | 31 +++++++ .../settingsServerSettingsZoneComponent.json | 29 ++++++ .../settings/settingsSideBarComponent.json | 10 +++ .../settings/settingsUserGoals.json | 7 ++ .../goalsAddEditGoalModalComponent.json | 30 +++++++ .../goalsListComponent.json | 5 ++ .../settingsUserProfileZoneComponent.json | 89 +++++++++++++++++++ .../userSessionsListComponent.json | 5 ++ .../usersAddEditUserModalComponent.json | 52 +++++++++++ ...usersChangeUserPasswordModalComponent.json | 10 +++ .../settingsUsersZone/usersListComponent.json | 22 +++++ .../usersPasswordRequirementsComponent.json | 7 ++ .../settings/settingsUsersZoneComponent.json | 10 +++ .../users/userDistanceStatsComponent.json | 4 + .../users/userGoalsStatsComponent.json | 13 +++ .../app/src/i18n/zh-CN/emailVerification.json | 14 +++ .../src/i18n/zh-CN/emailVerificationView.json | 8 ++ .../app/src/i18n/zh-CN/gears/gearView.json | 31 +++++++ .../app/src/i18n/zh-CN/gears/gearsView.json | 13 +++ frontend/app/src/i18n/zh-CN/generalItems.json | 75 ++++++++++++++++ frontend/app/src/i18n/zh-CN/healthView.json | 5 ++ frontend/app/src/i18n/zh-CN/homeView.json | 18 ++++ frontend/app/src/i18n/zh-CN/loginView.json | 37 ++++++++ frontend/app/src/i18n/zh-CN/notFoundView.json | 5 ++ .../app/src/i18n/zh-CN/resetPassword.json | 11 +++ frontend/app/src/i18n/zh-CN/searchView.json | 37 ++++++++ frontend/app/src/i18n/zh-CN/settingsView.json | 3 + frontend/app/src/i18n/zh-CN/signupView.json | 31 +++++++ .../i18n/zh-CN/strava/stravaCallbackView.json | 4 + frontend/app/src/i18n/zh-CN/summaryView.json | 44 +++++++++ frontend/app/src/i18n/zh-CN/userView.json | 32 +++++++ .../app/src/i18n/zh-TW/activitiesView.json | 14 +++ .../app/src/i18n/zh-TW/activityItems.json | 42 +++++++++ frontend/app/src/i18n/zh-TW/activityView.json | 13 +++ .../activities/activitiesTableComponent.json | 12 +++ .../activityBellowMPillsComponent.json | 25 ++++++ .../activities/activityLapsComponent.json | 14 +++ .../activityMandAbovePillsComponent.json | 17 ++++ .../activities/activityMapComponent.json | 8 ++ .../activities/activitySummaryComponent.json | 31 +++++++ .../activityWorkoutStepsComponent.json | 20 +++++ .../addGearToActivityModalComponent.json | 6 ++ .../modals/editActivityModalComponent.json | 68 ++++++++++++++ .../followers/followersListComponent.json | 16 ++++ .../gearComponentAddEditModalComponent.json | 19 ++++ .../gears/gearComponentListComponent.json | 79 ++++++++++++++++ .../gears/gearsAddEditGearModalComponent.json | 29 ++++++ .../components/gears/gearsListComponent.json | 16 ++++ .../health/healthDashboardZoneComponent.json | 13 +++ .../health/healthSideBarComponent.json | 4 + .../healthWeightAddEditModalComponent.json | 8 ++ .../healthWeightListComponent.json | 9 ++ .../health/healthWeightZoneComponent.json | 6 ++ .../navbar/navbarBottomMobileComponent.json | 7 ++ .../components/navbar/navbarComponent.json | 13 +++ .../components/noItemsFoundComponent.json | 4 + ...pApprovalRequestNotificationComponent.json | 4 + .../navbarNotificationsComponent.json | 6 ++ ...wAcceptedRequestNotificationComponent.json | 4 + ...plicateStartTimeNotificationComponent.json | 4 + .../newActivityNotificationComponent.json | 4 + ...wFollowerRequestNotificationComponent.json | 4 + .../settingsLanguageSwitcherComponent.json | 3 + .../settingsThemeSwitcherComponent.json | 6 ++ .../settings/settingsImportZoneComponent.json | 13 +++ .../garminConnectLoginModalComponent.json | 14 +++ .../settingsIntegrationsZoneComponent.json | 47 ++++++++++ .../settingsSecurityZoneComponent.json | 31 +++++++ .../settingsServerSettingsZoneComponent.json | 29 ++++++ .../settings/settingsSideBarComponent.json | 10 +++ .../settings/settingsUserGoals.json | 7 ++ .../goalsAddEditGoalModalComponent.json | 30 +++++++ .../goalsListComponent.json | 5 ++ .../settingsUserProfileZoneComponent.json | 89 +++++++++++++++++++ .../userSessionsListComponent.json | 5 ++ .../usersAddEditUserModalComponent.json | 52 +++++++++++ ...usersChangeUserPasswordModalComponent.json | 10 +++ .../settingsUsersZone/usersListComponent.json | 22 +++++ .../usersPasswordRequirementsComponent.json | 7 ++ .../settings/settingsUsersZoneComponent.json | 10 +++ .../users/userDistanceStatsComponent.json | 4 + .../users/userGoalsStatsComponent.json | 13 +++ .../app/src/i18n/zh-TW/emailVerification.json | 14 +++ .../src/i18n/zh-TW/emailVerificationView.json | 8 ++ .../app/src/i18n/zh-TW/gears/gearView.json | 31 +++++++ .../app/src/i18n/zh-TW/gears/gearsView.json | 13 +++ frontend/app/src/i18n/zh-TW/generalItems.json | 75 ++++++++++++++++ frontend/app/src/i18n/zh-TW/healthView.json | 5 ++ frontend/app/src/i18n/zh-TW/homeView.json | 18 ++++ frontend/app/src/i18n/zh-TW/loginView.json | 37 ++++++++ frontend/app/src/i18n/zh-TW/notFoundView.json | 5 ++ .../app/src/i18n/zh-TW/resetPassword.json | 11 +++ frontend/app/src/i18n/zh-TW/searchView.json | 37 ++++++++ frontend/app/src/i18n/zh-TW/settingsView.json | 3 + frontend/app/src/i18n/zh-TW/signupView.json | 31 +++++++ .../i18n/zh-TW/strava/stravaCallbackView.json | 4 + frontend/app/src/i18n/zh-TW/summaryView.json | 44 +++++++++ frontend/app/src/i18n/zh-TW/userView.json | 32 +++++++ frontend/app/src/views/SignUpView.vue | 6 +- 143 files changed, 2621 insertions(+), 7 deletions(-) create mode 100644 frontend/app/src/i18n/zh-CN/activitiesView.json create mode 100644 frontend/app/src/i18n/zh-CN/activityItems.json create mode 100644 frontend/app/src/i18n/zh-CN/activityView.json create mode 100644 frontend/app/src/i18n/zh-CN/components/activities/activitiesTableComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/activities/activityBellowMPillsComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/activities/activityLapsComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/activities/activityMandAbovePillsComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/activities/activityMapComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/activities/activitySummaryComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/activities/activityWorkoutStepsComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/activities/modals/addGearToActivityModalComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/activities/modals/editActivityModalComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/followers/followersListComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/gears/gearComponentAddEditModalComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/gears/gearComponentListComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/gears/gearsAddEditGearModalComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/gears/gearsListComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/health/healthDashboardZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/health/healthSideBarComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/health/healthWeightZone/healthWeightAddEditModalComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/health/healthWeightZone/healthWeightListComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/health/healthWeightZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/navbar/navbarBottomMobileComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/navbar/navbarComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/noItemsFoundComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/notifications/navbarNotificationsComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/notifications/newAcceptedRequestNotificationComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/notifications/newActivityDuplicateStartTimeNotificationComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/notifications/newActivityNotificationComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/notifications/newFollowerRequestNotificationComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsGeneralZone/settingsLanguageSwitcherComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsGeneralZone/settingsThemeSwitcherComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsImportZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsIntegrationsZone/garminConnectLoginModalComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsIntegrationsZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsSecurityZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsServerSettingsZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsSideBarComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoals.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoalsZone/goalsAddEditGoalModalComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoalsZone/goalsListComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsUserProfileZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsUserSessionsZone/userSessionsListComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersAddEditUserModalComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersChangeUserPasswordModalComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersListComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersPasswordRequirementsComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/users/userDistanceStatsComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/components/users/userGoalsStatsComponent.json create mode 100644 frontend/app/src/i18n/zh-CN/emailVerification.json create mode 100644 frontend/app/src/i18n/zh-CN/emailVerificationView.json create mode 100644 frontend/app/src/i18n/zh-CN/gears/gearView.json create mode 100644 frontend/app/src/i18n/zh-CN/gears/gearsView.json create mode 100644 frontend/app/src/i18n/zh-CN/generalItems.json create mode 100644 frontend/app/src/i18n/zh-CN/healthView.json create mode 100644 frontend/app/src/i18n/zh-CN/homeView.json create mode 100644 frontend/app/src/i18n/zh-CN/loginView.json create mode 100644 frontend/app/src/i18n/zh-CN/notFoundView.json create mode 100644 frontend/app/src/i18n/zh-CN/resetPassword.json create mode 100644 frontend/app/src/i18n/zh-CN/searchView.json create mode 100644 frontend/app/src/i18n/zh-CN/settingsView.json create mode 100644 frontend/app/src/i18n/zh-CN/signupView.json create mode 100644 frontend/app/src/i18n/zh-CN/strava/stravaCallbackView.json create mode 100644 frontend/app/src/i18n/zh-CN/summaryView.json create mode 100644 frontend/app/src/i18n/zh-CN/userView.json create mode 100644 frontend/app/src/i18n/zh-TW/activitiesView.json create mode 100644 frontend/app/src/i18n/zh-TW/activityItems.json create mode 100644 frontend/app/src/i18n/zh-TW/activityView.json create mode 100644 frontend/app/src/i18n/zh-TW/components/activities/activitiesTableComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/activities/activityBellowMPillsComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/activities/activityLapsComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/activities/activityMandAbovePillsComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/activities/activityMapComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/activities/activitySummaryComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/activities/activityWorkoutStepsComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/activities/modals/addGearToActivityModalComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/activities/modals/editActivityModalComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/followers/followersListComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/gears/gearComponentAddEditModalComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/gears/gearComponentListComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/gears/gearsAddEditGearModalComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/gears/gearsListComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/health/healthDashboardZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/health/healthSideBarComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/health/healthWeightZone/healthWeightAddEditModalComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/health/healthWeightZone/healthWeightListComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/health/healthWeightZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/navbar/navbarBottomMobileComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/navbar/navbarComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/noItemsFoundComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/notifications/navbarNotificationsComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/notifications/newAcceptedRequestNotificationComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/notifications/newActivityDuplicateStartTimeNotificationComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/notifications/newActivityNotificationComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/notifications/newFollowerRequestNotificationComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsGeneralZone/settingsLanguageSwitcherComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsGeneralZone/settingsThemeSwitcherComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsImportZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsIntegrationsZone/garminConnectLoginModalComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsIntegrationsZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsSecurityZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsServerSettingsZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsSideBarComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoals.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoalsZone/goalsAddEditGoalModalComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoalsZone/goalsListComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsUserProfileZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsUserSessionsZone/userSessionsListComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersAddEditUserModalComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersChangeUserPasswordModalComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersListComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersPasswordRequirementsComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZoneComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/users/userDistanceStatsComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/components/users/userGoalsStatsComponent.json create mode 100644 frontend/app/src/i18n/zh-TW/emailVerification.json create mode 100644 frontend/app/src/i18n/zh-TW/emailVerificationView.json create mode 100644 frontend/app/src/i18n/zh-TW/gears/gearView.json create mode 100644 frontend/app/src/i18n/zh-TW/gears/gearsView.json create mode 100644 frontend/app/src/i18n/zh-TW/generalItems.json create mode 100644 frontend/app/src/i18n/zh-TW/healthView.json create mode 100644 frontend/app/src/i18n/zh-TW/homeView.json create mode 100644 frontend/app/src/i18n/zh-TW/loginView.json create mode 100644 frontend/app/src/i18n/zh-TW/notFoundView.json create mode 100644 frontend/app/src/i18n/zh-TW/resetPassword.json create mode 100644 frontend/app/src/i18n/zh-TW/searchView.json create mode 100644 frontend/app/src/i18n/zh-TW/settingsView.json create mode 100644 frontend/app/src/i18n/zh-TW/signupView.json create mode 100644 frontend/app/src/i18n/zh-TW/strava/stravaCallbackView.json create mode 100644 frontend/app/src/i18n/zh-TW/summaryView.json create mode 100644 frontend/app/src/i18n/zh-TW/userView.json diff --git a/backend/app/users/user/schema.py b/backend/app/users/user/schema.py index 105462ddc..b0c9389f0 100644 --- a/backend/app/users/user/schema.py +++ b/backend/app/users/user/schema.py @@ -25,6 +25,8 @@ class Language(Enum): Members: CATALAN: Catalan language code ("ca"). + CHINESE_SIMPLIFIED: Simplified Chinese language code ("zh-CN"). + CHINESE_TRADITIONAL: Traditional Chinese language code ("zh-TW"). DUTCH: Dutch language code ("nl"). GERMAN: German language code ("de"). FRENCH: French language code ("fr"). @@ -34,6 +36,8 @@ class Language(Enum): """ CATALAN = "ca" + CHINESE_SIMPLIFIED = "zh-CN" + CHINESE_TRADITIONAL = "zh-TW" DUTCH = "nl" GERMAN = "de" FRENCH = "fr" diff --git a/frontend/app/src/components/Navbar/NavbarLanguageSwitcherComponent.vue b/frontend/app/src/components/Navbar/NavbarLanguageSwitcherComponent.vue index 6ea04c508..d3e5284d5 100644 --- a/frontend/app/src/components/Navbar/NavbarLanguageSwitcherComponent.vue +++ b/frontend/app/src/components/Navbar/NavbarLanguageSwitcherComponent.vue @@ -7,7 +7,9 @@ data-bs-toggle="dropdown" aria-expanded="false" > - + + + @@ -20,7 +22,9 @@ :aria-pressed="currentLanguage === language.value ? 'true' : 'false'" > {{ language.label }} - + + + [ { value: 'ca', label: t('generalItems.languageOption2') }, + { value: 'zh-CN', label: t('generalItems.languageOption8') }, + { value: 'zh-TW', label: t('generalItems.languageOption9') }, { value: 'de', label: t('generalItems.languageOption4') }, { value: 'fr', label: t('generalItems.languageOption5') }, { value: 'nl', label: t('generalItems.languageOption6') }, diff --git a/frontend/app/src/components/Settings/SettingsGeneralZone/SettingsLanguageSwitcherComponent.vue b/frontend/app/src/components/Settings/SettingsGeneralZone/SettingsLanguageSwitcherComponent.vue index c9a04e481..830906834 100644 --- a/frontend/app/src/components/Settings/SettingsGeneralZone/SettingsLanguageSwitcherComponent.vue +++ b/frontend/app/src/components/Settings/SettingsGeneralZone/SettingsLanguageSwitcherComponent.vue @@ -26,6 +26,8 @@ export default { const { locale, t } = useI18n() const languages = computed(() => [ { value: 'ca', label: t('generalItems.languageOption2') }, + { value: 'zh-CN', label: t('generalItems.languageOption8') }, + { value: 'zh-TW', label: t('generalItems.languageOption9') }, { value: 'de', label: t('generalItems.languageOption4') }, { value: 'fr', label: t('generalItems.languageOption5') }, { value: 'nl', label: t('generalItems.languageOption6') }, diff --git a/frontend/app/src/components/Settings/SettingsUserProfileZone.vue b/frontend/app/src/components/Settings/SettingsUserProfileZone.vue index d077d704c..32b0ba1a0 100644 --- a/frontend/app/src/components/Settings/SettingsUserProfileZone.vue +++ b/frontend/app/src/components/Settings/SettingsUserProfileZone.vue @@ -149,6 +149,12 @@ {{ $t('generalItems.languageOption2') }} + {{ + $t('generalItems.languageOption8') + }} + {{ + $t('generalItems.languageOption9') + }} {{ $t('generalItems.languageOption4') }} diff --git a/frontend/app/src/components/Settings/SettingsUsersZone/UsersAddEditUserModalComponent.vue b/frontend/app/src/components/Settings/SettingsUsersZone/UsersAddEditUserModalComponent.vue index 4a0cf5c59..da0f19bd6 100644 --- a/frontend/app/src/components/Settings/SettingsUsersZone/UsersAddEditUserModalComponent.vue +++ b/frontend/app/src/components/Settings/SettingsUsersZone/UsersAddEditUserModalComponent.vue @@ -326,6 +326,8 @@ required > + + diff --git a/frontend/app/src/i18n/index.js b/frontend/app/src/i18n/index.js index c86aaafd6..314b9f826 100644 --- a/frontend/app/src/i18n/index.js +++ b/frontend/app/src/i18n/index.js @@ -2,12 +2,12 @@ import { createI18n } from 'vue-i18n' // Bundle only locale JSON files (root + nested), eagerly so they're in dist const translationModules = { - ...import.meta.glob('./{ca,de,es,fr,nl,pt,us}/*.json', { eager: true }), - ...import.meta.glob('./{ca,de,es,fr,nl,pt,us}/**/*.json', { eager: true }) + ...import.meta.glob('./{ca,de,es,fr,nl,pt,us,zh-CN,zh-TW}/*.json', { eager: true }), + ...import.meta.glob('./{ca,de,es,fr,nl,pt,us,zh-CN,zh-TW}/**/*.json', { eager: true }) } // Define available locales -const locales = ['ca', 'de', 'es', 'fr', 'nl', 'pt', 'us'] +const locales = ['ca', 'de', 'es', 'fr', 'nl', 'pt', 'us', 'zh-CN', 'zh-TW'] // Define all component paths const componentPaths = { diff --git a/frontend/app/src/i18n/us/generalItems.json b/frontend/app/src/i18n/us/generalItems.json index c3c5ca41a..43a17bd3d 100644 --- a/frontend/app/src/i18n/us/generalItems.json +++ b/frontend/app/src/i18n/us/generalItems.json @@ -13,6 +13,8 @@ "languageOption5": "French (FR)", "languageOption6": "Dutch (NL)", "languageOption7": "Spanish (ES)", + "languageOption8": "Chinese (Simplified)", + "languageOption9": "Chinese (Traditional)", "firstDayOfWeekOption0": "Sunday", "firstDayOfWeekOption1": "Monday", "firstDayOfWeekOption2": "Tuesday", diff --git a/frontend/app/src/i18n/us/signupView.json b/frontend/app/src/i18n/us/signupView.json index bbfed0d78..969353c19 100644 --- a/frontend/app/src/i18n/us/signupView.json +++ b/frontend/app/src/i18n/us/signupView.json @@ -1,9 +1,11 @@ { + "title": "Sign Up", "subtitle": "Create your account below", "name": "Full Name", "username": "Username", "email": "Email Address", "password": "Password", + "optionalFields": " Optional Fields", "preferredLanguage": "Preferred Language", "city": "City", "birthdate": "Birth Date", diff --git a/frontend/app/src/i18n/zh-CN/activitiesView.json b/frontend/app/src/i18n/zh-CN/activitiesView.json new file mode 100644 index 000000000..3ca38492b --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/activitiesView.json @@ -0,0 +1,14 @@ +{ + "title": "Activities", + "filterLabelType": "Type", + "filterOptionAllTypes": "All types", + "filterLabelFromDate": "From date", + "filterLabelToDate": "To date", + "filterLabelNameLocation": "Name or location", + "filterPlaceholderNameLocation": "e.g., Morning run", + "buttonClear": "Clear", + "buttonApply": "Apply", + "errorFailedFetchActivityTypes": "Failed to fetch activity types", + "errorUpdatingActivities": "Failed to updated activities", + "errorFetchingActivities": "Failed to fetch user activities" +} diff --git a/frontend/app/src/i18n/zh-CN/activityItems.json b/frontend/app/src/i18n/zh-CN/activityItems.json new file mode 100644 index 000000000..0bf315638 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/activityItems.json @@ -0,0 +1,42 @@ +{ + "run": "Run", + "trailRun": "Trail run", + "virtualRun": "Virtual run", + "ride": "Ride", + "gravelRide": "Gravel ride", + "mtbRide": "MTB ride", + "virtualRide": "Virtual ride", + "lapSwimming": "Lap swimming", + "openWaterSwimming": "Open water swimming", + "workout": "Workout", + "walk": "Walk", + "indoorWalk": "Indoor walk", + "hike": "Hike", + "rowing": "Rowing", + "yoga": "Yoga", + "alpineSki": "Alpine ski", + "nordicSki": "Nordic ski", + "snowboard": "Snowboard", + "transition": "Transition", + "strengthTraining": "Strength training", + "crossfit": "CrossFit", + "tennis": "Tennis", + "tableTennis": "Table tennis", + "badminton": "Badminton", + "squash": "Squash", + "racquetball": "Racquetball", + "pickleball": "Pickleball", + "commutingRide": "Commuting ride", + "indoorRide": "Indoor ride", + "mixedSurfaceRide": "Mixed surface ride", + "windsurf": "Windsurf", + "standUpPaddling": "Stand up paddling", + "surf": "Surf", + "trackRun": "Track run", + "ebikeRide": "E-Bike ride", + "ebikeMountainRide": "E-Bike mountain ride", + "iceSkate": "Ice skate", + "soccer": "Soccer", + "padel": "Padel", + "labelWorkout": " workout" +} diff --git a/frontend/app/src/i18n/zh-CN/activityView.json b/frontend/app/src/i18n/zh-CN/activityView.json new file mode 100644 index 000000000..1def65a6a --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/activityView.json @@ -0,0 +1,13 @@ +{ + "labelGear": "Gear", + "labelGearNotSet": "Not set", + "modalLabelDeleteGear": "Delete gear from activity", + "modalLabelDeleteGearBody": "Are you sure you want to remove the gear from the activity?", + "modalLabelDeleteGearButton": "Delete gear", + "successMessageGearAdded": "Gear added to activity", + "successMessageGearDeleted": "Gear deleted from activity", + "errorMessageDeleteGear": "Error deleting gear from activity", + "errorMessageActivityNotFound": "Activity not found", + "alertPrivacyMessage": "You have hidden information in this activity. You can see it, but others cannot.", + "isHiddenMessage": "This activity is hidden. Probably because it is a duplicate or was hidden by the user." +} diff --git a/frontend/app/src/i18n/zh-CN/components/activities/activitiesTableComponent.json b/frontend/app/src/i18n/zh-CN/components/activities/activitiesTableComponent.json new file mode 100644 index 000000000..52f419c13 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/activities/activitiesTableComponent.json @@ -0,0 +1,12 @@ +{ + "headerType": "Type", + "headerName": "Name", + "headerLocation": "Location", + "headerStartTime": "Start time", + "headerDuration": "Duration", + "headerDistance": "Distance", + "headerPace": "Pace/Speed", + "headerCalories": "Calories", + "headerElevation": "Elevation", + "headerAvgHr": "Avg HR" +} diff --git a/frontend/app/src/i18n/zh-CN/components/activities/activityBellowMPillsComponent.json b/frontend/app/src/i18n/zh-CN/components/activities/activityBellowMPillsComponent.json new file mode 100644 index 000000000..00a22da7c --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/activities/activityBellowMPillsComponent.json @@ -0,0 +1,25 @@ +{ + "subTitlePace": "Pace", + "labelAvgPace": "Avg pace", + "labelMovingTime": "Moving time", + "labelElapsedTime": "Elapsed time", + "subTitleSpeed": "Speed", + "labelAvgSpeed": "Avg speed", + "labelMaxSpeed": "Max speed", + "subTitleHeartRate": "Heart rate", + "labelAvgHeartRate": "Avg heart rate", + "labelMaxHeartRate": "Max heart rate", + "subTitlePower": "Power", + "labelAvgPower": "Avg power", + "labelMaxPower": "Max power", + "labelNormalizedPower": "Normalized power", + "subTitleCadence": "Cadence", + "labelAvgCadence": "Avg cadence", + "labelMaxCadence": "Max cadence", + "subTitleElevation": "Elevation", + "labelElevationGain": "Elevation gain", + "labelElevationLoss": "Elevation loss", + "subTitleStrokeRate": "Stroke rate", + "labelAvgStrokeRate": "Avg stroke rate", + "labelMaxStrokeRate": "Max stroke rate" +} diff --git a/frontend/app/src/i18n/zh-CN/components/activities/activityLapsComponent.json b/frontend/app/src/i18n/zh-CN/components/activities/activityLapsComponent.json new file mode 100644 index 000000000..4318c6001 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/activities/activityLapsComponent.json @@ -0,0 +1,14 @@ +{ + "labelLapNumber": "Lap", + "labelLapIntensity": "Intensity", + "labelLapDistance": "Distance", + "labelLapTime": "Time", + "labelLapPace": "Pace", + "labelLapSpeed": "Speed", + "labelLapElevation": "Elevation", + "labelLapElev": "Elev", + "labelLapAvgHr": "Avg heart rate", + "labelLapHR": "HR", + "labelLapStrokeRate": "Stroke rate", + "labelLapSR": "SR" +} diff --git a/frontend/app/src/i18n/zh-CN/components/activities/activityMandAbovePillsComponent.json b/frontend/app/src/i18n/zh-CN/components/activities/activityMandAbovePillsComponent.json new file mode 100644 index 000000000..e04807141 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/activities/activityMandAbovePillsComponent.json @@ -0,0 +1,17 @@ +{ + "labelPillGraphs": "Graphs", + "labelPillLaps": "Laps", + "labelPillWorkoutSets": "Sets", + "labelGraph": "Activity data graphs", + "labelGraphHR": "Heart rate", + "labelHRZones": "Heart rate zones", + "labelGraphPower": "Power", + "labelGraphCadence": "Cadence", + "labelGraphElevation": "Elevation", + "labelGraphVelocity": "Speed", + "labelGraphPace": "Pace", + "labelGraphHRZone": "Zone", + "labelDownsampling": "Data downsampled to ~200 points", + "errorMessageProcessingActivityStreams": "Error processing activity streams", + "labelGraphStrokeRate": "Stroke rate" +} diff --git a/frontend/app/src/i18n/zh-CN/components/activities/activityMapComponent.json b/frontend/app/src/i18n/zh-CN/components/activities/activityMapComponent.json new file mode 100644 index 000000000..330d3e8ed --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/activities/activityMapComponent.json @@ -0,0 +1,8 @@ +{ + "modalMediaDeleteTitle": "Delete media", + "modalMediaDeleteBody1": "Are you sure you want to delete media with ID ", + "modalMediaDeleteBody2": " and name ", + "errorFetchingActivityStream": "Error fetching activity stream data", + "mediaDeletedSuccessfully": "Media deleted successfully", + "errorDeletingMedia": "Error deleting media" +} diff --git a/frontend/app/src/i18n/zh-CN/components/activities/activitySummaryComponent.json b/frontend/app/src/i18n/zh-CN/components/activities/activitySummaryComponent.json new file mode 100644 index 000000000..376d9bc63 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/activities/activitySummaryComponent.json @@ -0,0 +1,31 @@ +{ + "userNameHidden": "Hidden", + "visibilityPublic": "Public", + "visibilityFollowers": "Followers", + "visibilityPrivate": "Private", + "buttonDeleteActivity": "Delete activity", + "buttonEditActivity": "Edit activity", + "buttonAddActivityMedia": "Add media", + "modalDeleteBody1": "Are you sure you want to delete activity ", + "modalDeleteBody2": "This action cannot be undone.", + "modalAddMediaTitle": "Add media", + "modalAddMediaBody": "Upload .png, .jpg or .jpeg file", + "processingMediaUpload": "Processing media upload...", + "successMediaUpload": "Media uploaded successfully", + "errorMediaUpload": "Error uploading media", + "labelVirtual": "(Virtual) ", + "privateNotes": "Private notes", + "activityDistance": "Distance", + "activityTime": "Time", + "activityPace": "Pace", + "activityAvgHR": "Avg HR", + "activityMaxHR": "Max HR", + "activityAvgPower": "Avg power", + "activityAvgSpeed": "Avg speed", + "activityEleGain": "Ele gain", + "activityEleLoss": "Ele loss", + "activityCalories": "Calories", + "activityNoData": "No data", + "errorFetchingUserById": "Error fetching user by id", + "errorDeletingActivity": "Error deleting activity" +} diff --git a/frontend/app/src/i18n/zh-CN/components/activities/activityWorkoutStepsComponent.json b/frontend/app/src/i18n/zh-CN/components/activities/activityWorkoutStepsComponent.json new file mode 100644 index 000000000..509907d96 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/activities/activityWorkoutStepsComponent.json @@ -0,0 +1,20 @@ +{ + "labelWorkoutStepType": "Step type", + "labelWorkoutStepTime": "Step time", + "labelWorkoutStepReps": "Step reps", + "labelWorkoutStepIntensity": "Intensity", + "labelWorkoutStepNotes": "Notes", + "labelWorkoutStepExerciseName": "Step name", + "labelWorkoutStepExerciseWeight": "Weight", + "labelWorkoutStepSwimStroke": "Step swim stroke", + "labelWorkoutSetType": "Set type", + "labelWorkoutSetTime": "Set time", + "labelWorkoutSetReps": "Set reps", + "labelWorkoutSetExerciseName": "Set name", + "labelWorkoutSetExerciseWeight": "Set weight", + "labelWorkoutSetTypeMobile": "Type", + "labelWorkoutSetTimeMobile": "Time", + "labelWorkoutSetRepsMobile": "Reps", + "labelWorkoutSetExerciseNameMobile": "Name", + "labelWorkoutSetExerciseWeightMobile": "Weight" +} diff --git a/frontend/app/src/i18n/zh-CN/components/activities/modals/addGearToActivityModalComponent.json b/frontend/app/src/i18n/zh-CN/components/activities/modals/addGearToActivityModalComponent.json new file mode 100644 index 000000000..734c573b5 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/activities/modals/addGearToActivityModalComponent.json @@ -0,0 +1,6 @@ +{ + "modalLabelAddGear": "Add gear to activity", + "modalLabelSelectGear": "Select gear", + "modalButtonAddGear": "Add gear", + "errorEditingGear": "Error editing gear" +} diff --git a/frontend/app/src/i18n/zh-CN/components/activities/modals/editActivityModalComponent.json b/frontend/app/src/i18n/zh-CN/components/activities/modals/editActivityModalComponent.json new file mode 100644 index 000000000..e5f929c94 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/activities/modals/editActivityModalComponent.json @@ -0,0 +1,68 @@ +{ + "modalEditActivityTitle": "Edit activity", + "modalEditActivityDescriptionLabel": "Description", + "modalEditActivityDescriptionPlaceholder": "Description (max 2500 characters)", + "modalEditActivityPrivateNotesLabel": "Private notes", + "modalEditActivityPrivateNotesPlaceholder": "Private notes (max 2500 characters)", + "modalEditActivityNameLabel": "Name", + "modalEditActivityNamePlaceholder": "Name (max 250 characters)", + "modalEditActivityTypeLabel": "Type", + "modalEditActivityTypeOption1": "Run", + "modalEditActivityTypeOption2": "Trail run", + "modalEditActivityTypeOption3": "Virtual run", + "modalEditActivityTypeOption4": "Ride", + "modalEditActivityTypeOption5": "Gravel ride", + "modalEditActivityTypeOption6": "Mountain bike ride", + "modalEditActivityTypeOption7": "Virtual ride", + "modalEditActivityTypeOption8": "Swim", + "modalEditActivityTypeOption9": "Open water swim", + "modalEditActivityTypeOption10": "Workout", + "modalEditActivityTypeOption11": "Walk", + "modalEditActivityTypeOption12": "Hike", + "modalEditActivityTypeOption13": "Rowing", + "modalEditActivityTypeOption14": "Yoga", + "modalEditActivityTypeOption15": "Alpine skiing", + "modalEditActivityTypeOption16": "Nordic skiing", + "modalEditActivityTypeOption17": "Snowboarding", + "modalEditActivityTypeOption18": "Transition", + "modalEditActivityTypeOption19": "Weight training", + "modalEditActivityTypeOption20": "Crossfit", + "modalEditActivityTypeOption21": "Tennis", + "modalEditActivityTypeOption22": "Table tennis", + "modalEditActivityTypeOption23": "Badminton", + "modalEditActivityTypeOption24": "Squash", + "modalEditActivityTypeOption25": "Racquetball", + "modalEditActivityTypeOption26": "Pickleball", + "modalEditActivityTypeOption27": "Commuting ride", + "modalEditActivityTypeOption28": "Indoor ride", + "modalEditActivityTypeOption29": "Mixed surface ride", + "modalEditActivityTypeOption30": "Windsurf", + "modalEditActivityTypeOption31": "Indoor walk", + "modalEditActivityTypeOption32": "Stand up paddling", + "modalEditActivityTypeOption33": "Surf", + "modalEditActivityTypeOption34": "Track run", + "modalEditActivityTypeOption35": "E-Bike ride", + "modalEditActivityTypeOption36": "E-Bike mountain ride", + "modalEditActivityTypeOption37": "Ice skate", + "modalEditActivityTypeOption38": "Soccer", + "modalEditActivityTypeOption39": "Padel", + "modalEditActivityVisibilityLabel": "Visibility", + "modalEditActivityVisibilityOption0": "Public", + "modalEditActivityVisibilityOption1": "Followers", + "modalEditActivityVisibilityOption2": "Private", + "modalEditActivityIsHiddenLabel": "Is hidden", + "modalEditActivityHideStartTimeLabel": "Hide start time", + "modalEditActivityHideLocationLabel": "Hide location", + "modalEditActivityHideMapLabel": "Hide map", + "modalEditActivityHideHrLabel": "Hide heart rate", + "modalEditActivityHidePowerLabel": "Hide power", + "modalEditActivityHideCadenceLabel": "Hide cadence", + "modalEditActivityHideElevationLabel": "Hide elevation", + "modalEditActivityHideSpeedLabel": "Hide speed", + "modalEditActivityHidePaceLabel": "Hide pace", + "modalEditActivityHideLapsLabel": "Hide laps", + "modalEditActivityHideWorkoutSetsStepsLabel": "Hide workout sets/steps", + "modalEditActivityHideGearLabel": "Hide gear", + "successActivityEdit": "Activity edited successfully", + "errorActivityEdit": "Error editing activity" +} diff --git a/frontend/app/src/i18n/zh-CN/components/followers/followersListComponent.json b/frontend/app/src/i18n/zh-CN/components/followers/followersListComponent.json new file mode 100644 index 000000000..24c3b008d --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/followers/followersListComponent.json @@ -0,0 +1,16 @@ +{ + "requestAccepted": "Accepted", + "requestPending": "Request pending", + "followingModalTitle": "Delete following", + "followingModalBody": "Are you sure you want to delete following user ", + "followerModalTitle": "Delete follower", + "followerModalBody": "Are you sure you want to delete follower user ", + "followerAcceptModalTitle": "Accept user request", + "followerAcceptModalBody": "Are you sure you want to accept follow request from user ", + "followerDeclineModalTitle": "Decline user request ", + "followerDeclineModalBody": "Are you sure you want to decline follow request from user ", + "errorDeleteFollowing": "Error deleting following", + "errorDeleteFollower": "Error deleting follower", + "errorUpdateFollower": "Error updating follower", + "errorFetchingFollowersDetails": "Error fetching followers details" +} diff --git a/frontend/app/src/i18n/zh-CN/components/gears/gearComponentAddEditModalComponent.json b/frontend/app/src/i18n/zh-CN/components/gears/gearComponentAddEditModalComponent.json new file mode 100644 index 000000000..328c4f19a --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/gears/gearComponentAddEditModalComponent.json @@ -0,0 +1,19 @@ +{ + "addEditGearComponentModalAddTitle": "Add gear component", + "addEditGearComponentModalEditTitle": "Edit gear component", + "addEditGearComponentModalAddEditTypeLabel": "Type", + "addEditGearComponentModalAddEditBrandLabel": "Brand", + "addEditGearComponentModalAddEditModelLabel": "Model", + "addEditGearComponentModalAddEditPurchaseDateLabel": "Purchase date", + "addEditGearComponentModalAddEditExpectedDistanceLabel": "Expected distance", + "addEditGearComponentModalAddEditExpectedTimeLabel": "Expected time", + "addEditGearComponentModalAddEditPurchaseValueLabel": "Purchase value", + "addEditGearComponentModalAddEditRetiredDateLabel": "Retired date", + "addEditGearComponentModalAddEditIsActiveLabel": "Is active", + "successGearComponentAdded": "Gear component added successfully", + "successGearComponentEdited": "Gear component edited successfully", + "errorGearComponentAdd": "Error adding gear component", + "gearComponentListGearEditSuccessMessage": "Gear component edited successfully", + "gearComponentListGearEditErrorMessage": "Error editing gear component", + "retiredDateAfterPurchaseDateError": "Retired date must be after purchase date" +} diff --git a/frontend/app/src/i18n/zh-CN/components/gears/gearComponentListComponent.json b/frontend/app/src/i18n/zh-CN/components/gears/gearComponentListComponent.json new file mode 100644 index 000000000..802d9093b --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/gears/gearComponentListComponent.json @@ -0,0 +1,79 @@ +{ + "gearComponentBackTire": "Back tire", + "gearComponentFrontTire": "Front tire", + "gearComponentBackTube": "Back tube", + "gearComponentFrontTube": "Front tube", + "gearComponentBackWheelValve": "Back wheel valve", + "gearComponentFrontWheelValve": "Front wheel valve", + "gearComponentBackTubelessSealant": "Back tubeless sealant", + "gearComponentBackTubelessRimTape": "Back tubeless rim tape", + "gearComponentFrontTubelessSealant": "Front tubeless sealant", + "gearComponentFrontTubelessRimTape": "Front tubeless rim tape", + "gearComponentBackWheel": "Back wheel", + "gearComponentFrontWheel": "Front wheel", + "gearComponentBackBreakRotor": "Back brake rotor", + "gearComponentFrontBreakRotor": "Front brake rotor", + "gearComponentBackBreakPads": "Back brake pads", + "gearComponentFrontBreakPads": "Front brake pads", + "gearComponentBackBreakOil": "Back brake oil", + "gearComponentFrontBreakOil": "Front brake oil", + "gearComponentCrankLeftPowerMeter": "Crank left power meter", + "gearComponentCrankRightPowerMeter": "Crank right power meter", + "gearComponentCranksetPowerMeter": "Crankset power meter", + "gearComponentPedalsLeftPowerMeter": "Pedals left power meter", + "gearComponentPedalsRightPowerMeter": "Pedals right power meter", + "gearComponentPedalsPowerMeter": "Pedals power meter", + "gearComponentPedals": "Pedals", + "gearComponentCrankset": "Crankset", + "gearComponentCassette": "Cassette", + "gearComponentChain": "Chain", + "gearComponentFrontShifter": "Front shifter", + "gearComponentFrontDerailleur": "Front derailleur", + "gearComponentRearShifter": "Rear shifter", + "gearComponentRearDerailleur": "Rear derailleur", + "gearComponentBottomBracket": "Bottom bracket", + "gearComponentBottleCage": "Bottle cage", + "gearComponentHandlebar": "Handlebar", + "gearComponentHeadset": "Headset", + "gearComponentComputerMount": "Computer mount", + "gearComponentHandlebarTape": "Handlebar tape", + "gearComponentGrips": "Grips", + "gearComponentStem": "Stem", + "gearComponentSeatpost": "Seatpost", + "gearComponentSaddle": "Saddle", + "gearComponentFork": "Fork", + "gearComponentFrame": "Frame", + "gearComponentCleats": "Cleats", + "gearComponentInsoles": "Insoles", + "gearComponentLaces": "Laces", + "gearComponentBaseGrip": "Base grip", + "gearComponentBumpers": "Bumpers", + "gearComponentGrommets": "Grommets", + "gearComponentOverGrip": "Over grip", + "gearComponentStrings": "Strings", + "gearComponentSail": "Sail", + "gearComponentBoard": "Board", + "gearComponentMast": "Mast", + "gearComponentBoom": "Boom", + "gearComponentMastExtension": "Mast extension", + "gearComponentMastBase": "Mast base", + "gearComponentMastUniversalJoint": "Mast universal joint", + "gearComponentFin": "Fin", + "gearComponentFootstraps": "Footstraps", + "gearComponentHarnessLines": "Harness lines", + "gearComponentRiggingLines": "Rigging lines", + "gearComponentFootpad": "Footpad", + "gearComponentImpactVest": "Impact vest", + "gearComponentLifeguardVest": "Lifeguard vest", + "gearComponentHelmet": "Helmet", + "gearComponentWing": "Wing", + "gearComponentFrontFoil": "Front foil", + "gearComponentStabilizer": "Stabilizer", + "gearComponentFuselage": "Fuselage", + "gearComponentOf": " of ", + "gearComponentListGearComponentIsInactiveBadge": "Inactive", + "gearComponentListModalDeleteGearComponentTitle": "Delete gear component", + "gearComponentListModalDeleteGearComponentBody": "Are you sure you want to delete gear component ", + "gearComponentListGearDeleteSuccessMessage": "Gear component deleted successfully", + "gearComponentListGearDeleteErrorMessage": "Error deleting gear component" +} diff --git a/frontend/app/src/i18n/zh-CN/components/gears/gearsAddEditGearModalComponent.json b/frontend/app/src/i18n/zh-CN/components/gears/gearsAddEditGearModalComponent.json new file mode 100644 index 000000000..c8badac61 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/gears/gearsAddEditGearModalComponent.json @@ -0,0 +1,29 @@ +{ + "addEditGearModalEditTitle": "Edit gear", + "addEditGearModalAddTitle": "Add gear", + "addEditGearModalAddBrandLabel": "Brand", + "addEditGearModalAddModelLabel": "Model", + "addEditGearModalAddNicknameLabel": "Nickname", + "addEditGearModalAddTypeLabel": "Gear type", + "addEditGearModalAddTypeOption1": "Bike", + "addEditGearModalAddTypeOption2": "Shoes", + "addEditGearModalAddTypeOption3": "Wetsuit", + "addEditGearModalAddTypeOption4": "Racquet", + "addEditGearModalAddTypeOption5": "Skis", + "addEditGearModalAddTypeOption6": "Snowboard", + "addEditGearModalAddTypeOption7": "Windsurf", + "addEditGearModalAddTypeOption8": "Water sports board", + "addEditGearModalAddDateLabel": "Created date", + "addEditGearModalAddIsActiveLabel": "Is active", + "addEditGearModalAddIsActiveOption1": "Active", + "addEditGearModalAddIsActiveOption0": "Inactive", + "addEditGearModalAddIsInitialKmsLabel": "Initial kms", + "addEditGearModalAddIsInitialMilesLabel": "Initial miles", + "addEditGearModalAddEditPurchaseValueLabel": "Purchase value", + "errorNicknameAlreadyExistsFeedback": "Nickname already exists", + "errorNotPossibleToGetGearByNickname": "Is was not possible to get gear by nickname for validation", + "successGearAdded": "Gear added successfully", + "errorGearAdd": "Error adding gear", + "successGearEdited": "Gear edited successfully", + "errorGearEdit": "Error editing gear" +} diff --git a/frontend/app/src/i18n/zh-CN/components/gears/gearsListComponent.json b/frontend/app/src/i18n/zh-CN/components/gears/gearsListComponent.json new file mode 100644 index 000000000..d267f4e4c --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/gears/gearsListComponent.json @@ -0,0 +1,16 @@ +{ + "gearListTypeLabel": "Type", + "gearListTypeOption1": "Bike", + "gearListTypeOption2": "Shoes", + "gearListTypeOption3": "Wetsuit", + "gearListTypeOption4": "Racquet", + "gearListTypeOption5": "Skis", + "gearListTypeOption6": "Snowboard", + "gearListTypeOption7": "Windsurf", + "gearListTypeOption8": "Water sports board", + "gearListGearIsInactiveBadge": "Inactive", + "gearListModalDeleteGearTitle": "Delete gear", + "gearListModalDeleteGearBody": "Are you sure you want to delete gear ", + "gearListGearDeleteSuccessMessage": "Gear deleted successfully", + "gearListGearDeleteErrorMessage": "Error deleting gear" +} diff --git a/frontend/app/src/i18n/zh-CN/components/health/healthDashboardZoneComponent.json b/frontend/app/src/i18n/zh-CN/components/health/healthDashboardZoneComponent.json new file mode 100644 index 000000000..62a49e059 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/health/healthDashboardZoneComponent.json @@ -0,0 +1,13 @@ +{ + "weight": "Weight", + "noWeightData": "No weight data", + "noWeightTarget": "No weight target", + "noHeightDefined": "No height defined for user", + "bmi": "BMI", + "bmiUnderweight": "Underweight", + "bmiNormalWeight": "Normal weight", + "bmiOverweight": "Overweight", + "bmiObesityClass1": "Obesity (Class 1)", + "bmiObesityClass2": "Obesity (Class 2)", + "bmiObesityClass3": "Extreme Obesity (Class 3)" +} diff --git a/frontend/app/src/i18n/zh-CN/components/health/healthSideBarComponent.json b/frontend/app/src/i18n/zh-CN/components/health/healthSideBarComponent.json new file mode 100644 index 000000000..82d94300d --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/health/healthSideBarComponent.json @@ -0,0 +1,4 @@ +{ + "dashboardSection": "Dashboard", + "weightSection": "Weight" +} diff --git a/frontend/app/src/i18n/zh-CN/components/health/healthWeightZone/healthWeightAddEditModalComponent.json b/frontend/app/src/i18n/zh-CN/components/health/healthWeightZone/healthWeightAddEditModalComponent.json new file mode 100644 index 000000000..2af10b444 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/health/healthWeightZone/healthWeightAddEditModalComponent.json @@ -0,0 +1,8 @@ +{ + "addWeightModalTitle": "Add weight", + "editWeightModalTitle": "Edit weight", + "addWeightWeightLabel": "Weight", + "addWeightDateLabel": "Date", + "successAddWeight": "Weight added", + "errorAddWeight": "Error adding weight" +} diff --git a/frontend/app/src/i18n/zh-CN/components/health/healthWeightZone/healthWeightListComponent.json b/frontend/app/src/i18n/zh-CN/components/health/healthWeightZone/healthWeightListComponent.json new file mode 100644 index 000000000..5d457e51f --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/health/healthWeightZone/healthWeightListComponent.json @@ -0,0 +1,9 @@ +{ + "labelGarminConnect": "Garmin Connect", + "modalDeleteWeightTitle": "Delete weight", + "modalDeleteWeightBody": "Are you sure you want to delete weight entry for ", + "successDeleteWeight": "Weight deleted", + "errorDeleteWeight": "It was not possible to delete weight entry", + "successEditWeight": "Weight edited", + "errorEditWeight": "It was not possible to edit weight entry" +} diff --git a/frontend/app/src/i18n/zh-CN/components/health/healthWeightZoneComponent.json b/frontend/app/src/i18n/zh-CN/components/health/healthWeightZoneComponent.json new file mode 100644 index 000000000..8bcd11daa --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/health/healthWeightZoneComponent.json @@ -0,0 +1,6 @@ +{ + "buttonAddWeight": "Add weight", + "labelNumberOfHealthDataWeight1": "There is a total of ", + "labelNumberOfHealthDataWeight2": " weight measure(s) inserted (", + "labelNumberOfHealthDataWeight3": " loaded):" +} diff --git a/frontend/app/src/i18n/zh-CN/components/navbar/navbarBottomMobileComponent.json b/frontend/app/src/i18n/zh-CN/components/navbar/navbarBottomMobileComponent.json new file mode 100644 index 000000000..d164dd958 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/navbar/navbarBottomMobileComponent.json @@ -0,0 +1,7 @@ +{ + "home": "Home", + "gear": "Gear", + "health": "Health", + "alerts": "Alerts", + "menu": "Menu" +} diff --git a/frontend/app/src/i18n/zh-CN/components/navbar/navbarComponent.json b/frontend/app/src/i18n/zh-CN/components/navbar/navbarComponent.json new file mode 100644 index 000000000..487ba2305 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/navbar/navbarComponent.json @@ -0,0 +1,13 @@ +{ + "search": "Search", + "activities": "Activities", + "activitiesList": "List", + "summary": "Summary", + "gear": "Gear", + "health": "Health", + "profile": "Profile", + "settings": "Settings", + "login": "Login", + "logout": "Logout", + "errorLogout": "Error logging out" +} diff --git a/frontend/app/src/i18n/zh-CN/components/noItemsFoundComponent.json b/frontend/app/src/i18n/zh-CN/components/noItemsFoundComponent.json new file mode 100644 index 000000000..8daaee206 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/noItemsFoundComponent.json @@ -0,0 +1,4 @@ +{ + "title": "Ops...", + "subtitle": "No records found" +} diff --git a/frontend/app/src/i18n/zh-CN/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/zh-CN/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json new file mode 100644 index 000000000..5a675a94b --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -0,0 +1,4 @@ +{ + "title": "New sign-up request", + "subTitle": " has requested to sign-up" +} \ No newline at end of file diff --git a/frontend/app/src/i18n/zh-CN/components/notifications/navbarNotificationsComponent.json b/frontend/app/src/i18n/zh-CN/components/notifications/navbarNotificationsComponent.json new file mode 100644 index 000000000..8fdb7cb7d --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/notifications/navbarNotificationsComponent.json @@ -0,0 +1,6 @@ +{ + "errorFetchingNotificationsPagination": "Error fetching notifications with pagination", + "errorFetchingNotificationsNumber": "Error fetching notifications number", + "errorFetchingNotificationById": "Error fetching notification by ID", + "errorFetchingMessageFromWebSocket": "Error fetching message from WebSocket" +} diff --git a/frontend/app/src/i18n/zh-CN/components/notifications/newAcceptedRequestNotificationComponent.json b/frontend/app/src/i18n/zh-CN/components/notifications/newAcceptedRequestNotificationComponent.json new file mode 100644 index 000000000..0cd86fce6 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/notifications/newAcceptedRequestNotificationComponent.json @@ -0,0 +1,4 @@ +{ + "newAcceptedRequestTitle": "New accepted request", + "newAcceptedRequestSubTitle": " has accepted your follow request" +} diff --git a/frontend/app/src/i18n/zh-CN/components/notifications/newActivityDuplicateStartTimeNotificationComponent.json b/frontend/app/src/i18n/zh-CN/components/notifications/newActivityDuplicateStartTimeNotificationComponent.json new file mode 100644 index 000000000..be308a048 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/notifications/newActivityDuplicateStartTimeNotificationComponent.json @@ -0,0 +1,4 @@ +{ + "newActivityDuplicateStartTimeTitle": "New activity with duplicated start time", + "newActivityDuplicateStartTimeSubTitle": "A new activity has been added with a start time that overlaps with an existing activity. Please review it" +} diff --git a/frontend/app/src/i18n/zh-CN/components/notifications/newActivityNotificationComponent.json b/frontend/app/src/i18n/zh-CN/components/notifications/newActivityNotificationComponent.json new file mode 100644 index 000000000..3b5599c3a --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/notifications/newActivityNotificationComponent.json @@ -0,0 +1,4 @@ +{ + "newActivityTitle": "New activity", + "newActivitySubTitle": "Good job! A new activity has been added!" +} diff --git a/frontend/app/src/i18n/zh-CN/components/notifications/newFollowerRequestNotificationComponent.json b/frontend/app/src/i18n/zh-CN/components/notifications/newFollowerRequestNotificationComponent.json new file mode 100644 index 000000000..9d34f427b --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/notifications/newFollowerRequestNotificationComponent.json @@ -0,0 +1,4 @@ +{ + "newFollowerRequestTitle": "New follower request", + "newFollowerRequestSubTitle": "You have a new follower request from " +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsGeneralZone/settingsLanguageSwitcherComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsGeneralZone/settingsLanguageSwitcherComponent.json new file mode 100644 index 000000000..334897cd8 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsGeneralZone/settingsLanguageSwitcherComponent.json @@ -0,0 +1,3 @@ +{ + "formLabel": "Language" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsGeneralZone/settingsThemeSwitcherComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsGeneralZone/settingsThemeSwitcherComponent.json new file mode 100644 index 000000000..02516aca0 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsGeneralZone/settingsThemeSwitcherComponent.json @@ -0,0 +1,6 @@ +{ + "formLabel": "Theme", + "themeLight": "Light", + "themeDark": "Dark", + "themeAuto": "Auto" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsImportZoneComponent.json new file mode 100644 index 000000000..0fe6a9781 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsImportZoneComponent.json @@ -0,0 +1,13 @@ +{ + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", + "buttonBulkImport": "Import activities", + "loadingMessageBulkImport": "Importing activities from files...", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaGearImportTitle": "Strava gear import", + "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", + "stravaGearImportbuttonBikes": "Import Strava bikes", + "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", + "successMessageStravaBikesImport": "Strava bikes imported successfully", + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsIntegrationsZone/garminConnectLoginModalComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsIntegrationsZone/garminConnectLoginModalComponent.json new file mode 100644 index 000000000..01f90a110 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsIntegrationsZone/garminConnectLoginModalComponent.json @@ -0,0 +1,14 @@ +{ + "garminConnectAuthModalTitle": "Link Garmin Connect account", + "garminConnectAuthModalUsernameLabel": "Garmin Connect email", + "garminConnectAuthModalUsernamePlaceholder": "Garmin Connect email", + "garminConnectAuthModalPasswordLabel": "Garmin Connect password", + "garminConnectAuthModalPasswordPlaceholder": "Garmin Connect password", + "garminConnectAuthModalMfaCodeLabel": "MFA code", + "garminConnectAuthModalMfaCodePlaceholder": "MFA code", + "buttonSubmitMfaCode": "Submit MFA code", + "garminConnectAuthModalLoginButton": "Login", + "processingMessageLinkGarminConnect": "Linking Garmin Connect account...", + "successMessageLinkGarminConnect": "Garmin Connect account linked", + "errorMessageUnableToLinkGarminConnect": "Unable to link Garmin Connect account" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsIntegrationsZoneComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsIntegrationsZoneComponent.json new file mode 100644 index 000000000..ab40baa88 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsIntegrationsZoneComponent.json @@ -0,0 +1,47 @@ +{ + "stravaIntegrationTitle": "Strava", + "stravaIntegrationBody": "Strava is an American internet service for tracking physical exercise which incorporates social network features.", + "buttonConnect": "Connect", + "buttonDropdownOptions": "Options", + "modalRetrieveActivitiesByDaysTitle": "Retrieve activities by days", + "modalRetrieveActivitiesByDateRangeTitle": "Retrieve activities by date range", + "modalRetrieveActivitiesByDaysLabel": "Days", + "modalRetrieveActivitiesByDaysPlaceholder": "Days", + "modalRetrieveButton": "Retrieve", + "buttonRetrieveGear": "Retrieve gear", + "buttonRelink": "Relink", + "buttonUnlink": "Unlink", + "modalRetrieveClientIdTitle": "Connect Strava", + "modalRetrieveClientIdLabel": "Client ID", + "modalRetrieveClientSecretLabel": "Client secret", + "errorMessageUnableToLinkStrava": "Unable to link Strava account", + "errorMessageUnableToUnsetStravaClientSettings": "Unable to unset Strava client and state settings after linking error", + "successMessageStravaAccountLinked": "Strava account linked", + "errorMessageUnableToUnSetStravaState": "Unable to unset Strava state", + "errorMessageUnableToGetStravaActivities": "Unable to get Strava activities", + "errorMessageUnableToGetStravaGear": "Unable to get Strava gear", + "loadingMessageRetrievingStravaActivities": "Retrieving Strava activities", + "loadingMessageRetrievingStravaGear": "Retrieving Strava gear", + "processingMessageUnlinkStrava": "Unlinking Strava account...", + "successMessageStravaUnlinked": "Strava account unlinked", + "errorMessageUnableToUnlinkStrava": "Unable to unlink Strava account", + "modalUnlinkStravaTitle": "Unlink Strava", + "modalUnlinkStravaBody": "Are you sure you want to unlink your Strava account? Unlinking your Strava account will remove all your Strava activities and gear from Endurain.", + "garminConnectIntegrationTitle": "Garmin Connect", + "garminConnectIntegrationBody": "Garmin Connect is a health and fitness activity platform for users of Garmin devices", + "loadingMessageRetrievingGarminConnectActivities": "Retrieving Garmin Connect activities", + "errorMessageUnableToGetGarminConnectActivitiesDays": "Unable to get Garmin Connect activities by days", + "errorMessageUnableToGetGarminConnectActivitiesDataRange": "Unable to get Garmin Connect activities using data range", + "modalUnlinkGarminConnectTitle": "Unlink Garmin Connect", + "modalUnlinkGarminConnectBody": "Are you sure you want to unlink your Garmin Connect account?", + "processingMessageUnlinkGarminConnect": "Unlinking Garmin Connect account...", + "successMessageGarminConnectUnlinked": "Garmin Connect account unlinked", + "errorMessageUnableToUnlinkGarminConnect": "Unable to unlink Garmin Connect account", + "errorMessageUnableToGetGarminConnectGear": "Unable to get Garmin Connect gear", + "loadingMessageRetrievingGarminConnectGear": "Retrieving Garmin Connect gear", + "modalRetrieveHealthDataByDaysTitle": "Retrieve health data by days", + "modalRetrieveHealthDataByDateRangeTitle": "Retrieve health data by date range", + "errorMessageUnableToGetGarminConnectHealthDataDays": "Unable to get Garmin Connect health data by days", + "errorMessageUnableToGetGarminConnectHealthDataDateRange": "Unable to get Garmin Connect health data using date range", + "loadingMessageRetrievingGarminConnectHealthData": "Retrieving Garmin Connect health data" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsSecurityZoneComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsSecurityZoneComponent.json new file mode 100644 index 000000000..cab71a900 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsSecurityZoneComponent.json @@ -0,0 +1,31 @@ +{ + "subtitleChangePassword": "Change password", + "changeUserPasswordBodyLabel": "Change password for user ", + "changeUserPasswordPasswordLabel": "New password", + "changeUserPasswordPasswordConfirmationLabel": "Confirm new password", + "changeUserPasswordFeedbackLabel": "Password does not meet requirements", + "changeUserPasswordPasswordsDoNotMatchFeedbackLabel": "Passwords do not match", + "subtitleMFA": "Multi-Factor Authentication (MFA)", + "mfaDisabledDescription": "MFA is currently disabled. Enable it to add an extra layer of security to your account.", + "mfaEnabledDescription": "MFA is currently enabled. Your account is protected with two-factor authentication.", + "enableMFAButton": "Enable MFA", + "disableMFAButton": "Disable MFA", + "mfaSetupModalTitle": "Setup Multi-Factor Authentication", + "mfaSetupInstructions": "Scan the QR code below with your authenticator app (Google Authenticator, Authy, etc.) or manually enter the secret:", + "mfaSecretLabel": "Secret Key", + "mfaVerificationCodeLabel": "Verification Code", + "mfaVerificationCodePlaceholder": "Enter 6-digit code", + "mfaDisableModalTitle": "Disable Multi-Factor Authentication", + "mfaDisableConfirmation": "Are you sure you want to disable MFA? This will reduce your account security.", + "mfaEnabledSuccess": "MFA enabled successfully", + "mfaDisabledSuccess": "MFA disabled successfully", + "errorLoadMFAStatus": "Error loading MFA status", + "errorSetupMFA": "Error setting up MFA", + "errorEnableMFA": "Error enabling MFA", + "errorDisableMFA": "Error disabling MFA", + "subtitleMySessions": "My sessions", + "userChangePasswordSuccessMessage": "Password changed successfully", + "userChangePasswordErrorMessage": "Error changing password", + "successDeleteSession": "Session deleted successfully", + "errorDeleteSession": "Error deleting session" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsServerSettingsZoneComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsServerSettingsZoneComponent.json new file mode 100644 index 000000000..1aaf373ae --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsServerSettingsZoneComponent.json @@ -0,0 +1,29 @@ +{ + "defaultsTitle": "Defaults", + "unitsLabel": "Default units", + "unitsMetric": "Metric", + "unitsImperial": "Imperial", + "currencyLabel": "Default currency", + "numRecordsLabel": "Number of records per page", + "signupTitle": "Sign-up", + "adminApprovalLabel": "Admin approval", + "emailConfirmationLabel": "Email confirmation", + "publicShareableLinksLabel": "Public shareable links", + "enabledLabel": "Enabled", + "serverSettingsPublicShareableLinksEnabledWarningAlert": "Enabling this will make all publicly posted activities viewable without authentication.", + "publicShareableLinksShowUserInfoLabel": "Show user info", + "serverSettingsPublicShareableLinksShowUserWarningAlert": "Enabling this will display user information on all public links", + "photosLabel": "Photos", + "loginPhotoLabel": "Login photo", + "buttonAddPhoto": "Add photo", + "logonPhotoAddLabel": "Login page photo (.png) with a size of 1000x1000 pixels.", + "processingPhotoUpload": "Processing photo upload", + "successPhotoUpload": "Photo uploaded successfully", + "buttonDeleteLoginPhoto": "Delete login photo", + "modalDeleteLoginPhotoBody": "Are you sure you want to delete the login photo?", + "processingPhotoDelete": "Processing photo delete", + "successPhotoDelete": "Photo deleted successfully", + "successUpdateServerSettings": "Server settings updated successfully", + "errorUpdateServerSettings": "Error updating server settings", + "errorFetchingServerSettings": "Error fetching server settings" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsSideBarComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsSideBarComponent.json new file mode 100644 index 000000000..e43abd6c3 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsSideBarComponent.json @@ -0,0 +1,10 @@ +{ + "usersSection": "Users", + "serverSettingsSection": "Server Settings", + "generalSection": "General", + "myProfileSection": "My Profile", + "myGoals": "My Goals", + "securitySection": "Security", + "integrationsSection": "Integrations", + "importSection": "Import" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoals.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoals.json new file mode 100644 index 000000000..2f48bad9d --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoals.json @@ -0,0 +1,7 @@ +{ + "addNewGoal": "Add new goal", + "labelNumberOfGoals1": "You have ", + "labelNumberOfGoals2": " goal(s) set:", + "successGoalDeleted": "Goal deleted successfully", + "errorFetchingGoals": "Error fetching goals" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoalsZone/goalsAddEditGoalModalComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoalsZone/goalsAddEditGoalModalComponent.json new file mode 100644 index 000000000..26c341d8d --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoalsZone/goalsAddEditGoalModalComponent.json @@ -0,0 +1,30 @@ +{ + "addEditGoalModalAddTitle": "Add Goal", + "addEditGoalModalEditTitle": "Edit Goal", + "addEditGoalModalGoalIntervalLabel": "Interval", + "intervalOption1": "Daily", + "intervalOption2": "Weekly", + "intervalOption3": "Monthly", + "intervalOption4": "Yearly", + "addEditGoalModalGoalActivityTypeLabel": "Activity Type", + "activityTypeRun": "Run", + "activityTypeBike": "Bike", + "activityTypeSwim": "Swim", + "activityTypeWalk": "Walk", + "activityTypeStrength": "Strength", + "addEditGoalModalGoalTypeLabel": "Type", + "addEditGoalModalCaloriesLabel": "Calories", + "addEditGoalModalCaloriesPlaceholder": "Target calories", + "addEditGoalModalActivitiesNumberLabel": "Activities number", + "addEditGoalModalActivitiesNumberPlaceholder": "Target activities number", + "addEditGoalModalDistanceLabel": "Distance", + "addEditGoalModalDistancePlaceholder": "Target distance", + "addEditGoalModalElevationLabel": "Elevation", + "addEditGoalModalElevationPlaceholder": "Target elevation", + "addEditGoalModalDurationLabel": "Duration", + "addEditGoalModalDurationPlaceholder": "Target duration (ex: 1.5 equals 1h30m)", + "addEditGoalModalSuccessAddGoal": "Goal added successfully", + "addEditGoalModalErrorAddGoal": "Error adding goal", + "addEditGoalModalSuccessEditGoal": "Goal edited successfully", + "addEditGoalModalErrorEditGoal": "Error editing goal" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoalsZone/goalsListComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoalsZone/goalsListComponent.json new file mode 100644 index 000000000..b8880e31d --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsUserGoalsZone/goalsListComponent.json @@ -0,0 +1,5 @@ +{ + "modalDeleteGoalTitle": "Delete Goal", + "modalDeleteGoalBody": "Are you sure you want to delete the goal with ID ", + "goalDeleteErrorMessage": "Error deleting goal" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsUserProfileZoneComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsUserProfileZoneComponent.json new file mode 100644 index 000000000..60ea305c6 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsUserProfileZoneComponent.json @@ -0,0 +1,89 @@ +{ + "titleProfileInfo": "My profile", + "buttonDeleteProfilePhoto": "Delete", + "modalDeleteProfilePhotoBody": "Are you sure you want to delete your profile photo?", + "buttonEditProfile": "Profile", + "usernameLabel": "Username", + "emailLabel": "Email", + "cityLabel": "City", + "birthdayLabel": "Birthday", + "genderLabel": "Gender", + "genderOption1": "Male", + "genderOption2": "Female", + "genderOption3": "Unspecified", + "unitsLabel": "Units", + "unitsOption1": "Metric", + "unitsOption2": "Imperial", + "currencyLabel": "Currency", + "heightLabel": "Height", + "preferredLanguageLabel": "Preferred language", + "firstDayOfWeekLabel": "First day of week", + "accessTypeLabel": "Access type", + "accessTypeOption1": "Regular user", + "accessTypeOption2": "Administrator", + "userPhotoDeleteSuccess": "Profile photo deleted successfully", + "userPhotoDeleteError": "Error deleting profile photo", + "titleDefaultGear": "Default gear", + "subTitleShoeActivities": "Shoe activities", + "subTitleRun": "Run", + "subTitleTrailRun": "Trail run", + "subTitleVirtualRun": "Virtual run", + "subTitleWalk": "Walk", + "subTitleHike": "Hike", + "subTitleBikeActivities": "Bike activities", + "subTitleBike": "Bike", + "subTitleMTBBike": "MTB bike", + "subTitleGravelBike": "Gravel bike", + "subTitleVirtualBike": "Virtual bike", + "subTitleWaterActivities": "Water activities", + "subTitleSwim": "Open water swim", + "subTitleWindsurf": "Windsurf", + "subTitleRacquetActivities": "Racquet activities", + "subTitleTennis": "Tennis", + "subTitleSnowActivities": "Snow activities", + "subTitleAlpineSki": "Alpine ski", + "subTitleNordicSki": "Nordic ski", + "subTitleSnowboard": "Snowboard", + "selectOptionNotDefined": "Not defined", + "titlePrivacy": "Privacy", + "defaultActivityVisibility": "Default activity visibility", + "privacyOption1": "Public", + "privacyOption2": "Followers", + "privacyOption3": "Private", + "defaultActivityStartTime": "Hide activity start time", + "defaultActivityLocation": "Hide activity location", + "defaultActivityMap": "Hide activity map", + "defaultActivityHeartRate": "Hide activity heart rate", + "defaultActivityPower": "Hide activity power", + "defaultActivityCadence": "Hide activity cadence", + "defaultActivityElevation": "Hide activity elevation", + "defaultActivitySpeed": "Hide activity speed", + "defaultActivityPace": "Hide activity pace", + "defaultActivityLaps": "Hide activity laps", + "defaultActivitySetsSteps": "Hide activity sets/steps", + "defaultActivityGear": "Hide activity gear", + "buttonChangeDefaultActivityVisibility": "Change default visibility", + "buttonChangeUserActivitiesVisibility": "Change activities visibility", + "changeUserActivitiesVisibilityModalVisibilityLabel": "Visibility", + "changeUserActivitiesVisibilityModalButton": "Change", + "successUpdateUserActivitiesVisibility": "Activities visibility updated successfully", + "errorUpdateUserActivitiesVisibility": "Error updating activities visibility", + "errorUnableToGetGear": "Unable to get gear", + "errorUnableToGetDefaultGear": "Unable to get default gear", + "successUpdateDefaultGear": "Default gear updated successfully", + "errorUpdateDefaultGear": "Error updating default gear", + "successUpdateUserPrivacySettings": "User privacy settings updated successfully", + "errorUpdateUserPrivacySettings": "Error updating user privacy settings", + "titleExportData": "Export and import data", + "labelPasswordDisclaimer": "User password is not included in the import/export process. After import, update your password on security section.", + "buttonExportData": "Export data", + "buttonImportData": "Import data", + "modalImportTitle": "Import backup file", + "modalImportBody": "Select the .zip file you previously exported to restore your data.", + "exportLoading": "Exporting data, this may take a few minutes...", + "exportSuccess": "Export completed successfully", + "exportError": "Error exporting data", + "importLoading": "Importing data, this may take a few minutes...", + "importSuccess": "Import completed", + "importError": "Error importing data" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsUserSessionsZone/userSessionsListComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsUserSessionsZone/userSessionsListComponent.json new file mode 100644 index 000000000..3a87aca5f --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsUserSessionsZone/userSessionsListComponent.json @@ -0,0 +1,5 @@ +{ + "badgeCurrentSession": "Current Session", + "modalDeleteSessionTitle": "Delete Session", + "modalDeleteSessionBody": "Are you sure you want to delete session " +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersAddEditUserModalComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersAddEditUserModalComponent.json new file mode 100644 index 000000000..f9ccb345a --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersAddEditUserModalComponent.json @@ -0,0 +1,52 @@ +{ + "addEditUserModalAddTitle": "Add user", + "addEditUserModalEditTitle": "Edit user", + "addEditUserModalEditProfileTitle": "Edit profile", + "addEditUserModalDeleteUserPhotoButton": "Delete photo", + "addEditUserModalUserPhotoLabel": "User photo", + "addEditUserModalUsernameLabel": "Username", + "addEditUserModalUsernamePlaceholder": "Username (max 45 characters)", + "addEditUserModalErrorUsernameExists": "Username already exists", + "addEditUserModalNameLabel": "Name", + "addEditUserModalNamePlaceholder": "Name (max 45 characters)", + "addEditUserModalEmailLabel": "Email", + "addEditUserModalEmailPlaceholder": "Email (max 45 characters)", + "addEditUserModalErrorEmailInvalid": "Email is not valid", + "addEditUserModalErrorEmailExists": "Email already exists", + "addEditUserModalPasswordLabel": "Password", + "addEditUserModalPasswordPlaceholder": "Password", + "addEditUserModalErrorPasswordInvalid": "Password does not meet requirements", + "addEditUserModalCityLabel": "City", + "addEditUserModalCityPlaceholder": "City (max 45 characters)", + "addEditUserModalBirthdayLabel": "Birthday", + "addEditUserModalGenderLabel": "Gender", + "addEditUserModalUnitsLabel": "Units", + "addEditUserModalUnitsOption1": "Metric", + "addEditUserModalUnitsOption2": "Imperial", + "addEditUserModalCurrencyLabel": "Currency", + "addEditUserModalHeightLabel": "Height", + "addEditUserModalHeightPlaceholder": "Height", + "addEditUserModalFeetValidationLabel": "Invalid height. Please enter a valid height in feet.", + "addEditUserModalInchesValidationLabel": "Invalid height. Please enter a valid height in inches.", + "addEditUserModalUserPreferredLanguageLabel": "Preferred language", + "addEditUserModalUserFirstDayOfWeekLabel": "First day of week", + "addEditUserModalUserTypeLabel": "Access type", + "addEditUserModalUserTypeOption1": "Regular user", + "addEditUserModalUserTypeOption2": "Administrator", + "addEditUserModalIsActiveLabel": "Is active", + "addEditUserModalIsActiveOption1": "Yes", + "addEditUserModalIsActiveOption2": "No", + "addEditUserModalDefaultActivityVisibilityLabel": "Default activity visibility", + "addEditUserModalDefaultActivityVisibilityOption1": "Public", + "addEditUserModalDefaultActivityVisibilityOption2": "Followers", + "addEditUserModalDefaultActivityVisibilityOption3": "Private", + "addEditUserModalErrorFetchingUserByUsername": "Error fetching user by username", + "addEditUserModalErrorFetchingUserByEmail": "Error fetching user by email", + "addEditUserModalSuccessDeleteUserPhoto": "User photo deleted successfully", + "addEditUserModalErrorDeleteUserPhoto": "Error deleting user photo", + "addEditUserModalErrorUploadingUserPhoto": "Error uploading user photo", + "addEditUserModalSuccessAddUser": "User added successfully", + "addEditUserModalErrorAddUser": "Error adding user", + "addEditUserModalSuccessEditUser": "User edited successfully", + "addEditUserModalErrorEditUser": "Error editing user" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersChangeUserPasswordModalComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersChangeUserPasswordModalComponent.json new file mode 100644 index 000000000..9ad092ed0 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersChangeUserPasswordModalComponent.json @@ -0,0 +1,10 @@ +{ + "modalChangeUserPasswordTitle": "Change user password", + "modalChangeUserPasswordBodyLabel": "Change password for user ", + "modalChangeUserPasswordPasswordLabel": "New password", + "modalChangeUserPasswordPasswordConfirmationLabel": "Confirm new password", + "modalChangeUserPasswordFeedbackLabel": "Password does not meet requirements", + "modalChangeUserPasswordPasswordsDoNotMatchFeedbackLabel": "Passwords do not match", + "userChangePasswordSuccessMessage": "Password changed successfully", + "userChangePasswordErrorMessage": "Error changing password" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersListComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersListComponent.json new file mode 100644 index 000000000..95080416a --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersListComponent.json @@ -0,0 +1,22 @@ +{ + "userListAccessTypeOption1": "Regular user", + "userListAccessTypeOption2": "Administrator", + "userListUserIsMeBadge": "Me", + "userListUserIsAdminBadge": "Admin", + "userListUserIsInactiveBadge": "Inactive", + "userListUserHasUnverifiedEmailBadge": "Unverified email", + "modalApproveSignUpTitle": "Approve user sign up", + "modalApproveSignUpBody": "Are you sure you want to approve user ", + "processingApproval": "Processing approval...", + "userApproveSuccessMessage": "User approved successfully.", + "userApproveErrorMessage": "Error approving user", + "modalRejectSignUpTitle": "Reject user sign up", + "modalRejectSignUpBody1": "Are you sure you want to reject user ", + "modalRejectSignUpBody2": "The user will be deleted, this cannot be undone.", + "userEditErrorMessage": "Error editing user", + "modalDeleteUserTitle": "Delete user", + "modalDeleteUserBody": "Are you sure you want to delete user ", + "userListUserSessionsTitle": "User sessions", + "userSessionDeleteSuccessMessage": "Session deleted successfully", + "userSessionDeleteErrorMessage": "Error deleting session" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersPasswordRequirementsComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersPasswordRequirementsComponent.json new file mode 100644 index 000000000..f6ebfe887 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZone/usersPasswordRequirementsComponent.json @@ -0,0 +1,7 @@ +{ + "passwordRequirementsTitle": "Password requirements includes:", + "passwordCharacters": "- 8 characters;", + "passwordCapitalLetters": "- 1 capital letter;", + "passwordNumbers": "- 1 number;", + "passwordSpecialCharacters": "- 1 special character;" +} diff --git a/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZoneComponent.json b/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZoneComponent.json new file mode 100644 index 000000000..9b0245cbc --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/settings/settingsUsersZoneComponent.json @@ -0,0 +1,10 @@ +{ + "buttonAddUser": "Add user", + "labelSearchUsersByUsername": "Search users by username", + "labelNumberOfUsers1": "There is a total of ", + "labelNumberOfUsers2": " user(s) (", + "labelNumberOfUsers3": " loaded):", + "successUserAdded": "User added successfully", + "successUserDeleted": "User deleted successfully", + "errorFetchingUsers": "Error fetching users" +} diff --git a/frontend/app/src/i18n/zh-CN/components/users/userDistanceStatsComponent.json b/frontend/app/src/i18n/zh-CN/components/users/userDistanceStatsComponent.json new file mode 100644 index 000000000..be1abc1ff --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/users/userDistanceStatsComponent.json @@ -0,0 +1,4 @@ +{ + "thisWeekDistancesTitle": "Week top 3", + "thisMonthDistancesTitle": "Month top 3" +} diff --git a/frontend/app/src/i18n/zh-CN/components/users/userGoalsStatsComponent.json b/frontend/app/src/i18n/zh-CN/components/users/userGoalsStatsComponent.json new file mode 100644 index 000000000..a7391a0bd --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/components/users/userGoalsStatsComponent.json @@ -0,0 +1,13 @@ +{ + "title": "Goals", + "activityTypeRun": "Run", + "activityTypeBike": "Bike", + "activityTypeSwim": "Swim", + "activityTypeWalk": "Walk", + "activityTypeStrength": "Strength", + "intervalOption1": "Daily", + "intervalOption2": "Weekly", + "intervalOption3": "Monthly", + "intervalOption4": "Yearly", + "activities": "activities" +} diff --git a/frontend/app/src/i18n/zh-CN/emailVerification.json b/frontend/app/src/i18n/zh-CN/emailVerification.json new file mode 100644 index 000000000..8317204c6 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/emailVerification.json @@ -0,0 +1,14 @@ +{ + "verifying": "Verifying your email...", + "pleaseWait": "Please wait while we verify your email address", + "success": "Email Verified!", + "error": "Verification Failed", + "goToLogin": "Go to Login", + "backToLogin": "Back to Login", + "invalidToken": "Invalid or missing verification token", + "verificationSuccess": "Your email has been successfully verified", + "tokenNotFound": "Invalid or expired verification token", + "tokenExpired": "Verification token has expired", + "verificationFailed": "Email verification failed. Please try again", + "emailVerified": "Email verified successfully" +} \ No newline at end of file diff --git a/frontend/app/src/i18n/zh-CN/emailVerificationView.json b/frontend/app/src/i18n/zh-CN/emailVerificationView.json new file mode 100644 index 000000000..7815a375d --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/emailVerificationView.json @@ -0,0 +1,8 @@ +{ + "title1": "Handling verify email", + "title2": "Please wait while your email is being verified. Do not refresh this page.", + "emailVerified": "Email verified successfully!", + "tokenNotFound": "Token not found", + "tokenExpired": "Token expired", + "verificationFailed": "Email verification failed" +} \ No newline at end of file diff --git a/frontend/app/src/i18n/zh-CN/gears/gearView.json b/frontend/app/src/i18n/zh-CN/gears/gearView.json new file mode 100644 index 000000000..fde20a2ea --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/gears/gearView.json @@ -0,0 +1,31 @@ +{ + "buttonAddComponent": "Add component", + "buttonEditGear": "Edit gear", + "buttonDeleteGear": "Delete gear", + "modalDeleteGearBody1": "Are you sure you want to delete gear", + "modalDeleteGearBody2": "This action cannot be undone.", + "gearIsActiveBadge": "Active", + "gearIsInactiveBadge": "Inactive", + "gearTypeOption1": "Bike", + "gearTypeOption2": "Shoes", + "gearTypeOption3": "Wetsuit", + "gearTypeOption4": "Racquet", + "gearTypeOption5": "Skis", + "gearTypeOption6": "Snowboard", + "gearTypeOption7": "Windsurf", + "gearTypeOption8": "Water sports board", + "gearFromStrava": "Strava", + "gearFromGarminConnect": "Garmin Connect", + "labelBrand": "Brand", + "labelModel": "Model", + "labelPurchaseValue": "Purchase value", + "labelTotalCost": "Total cost", + "labelDistance": "Distance", + "labelTime": "Time", + "titleComponents": "Components", + "showInactiveComponents": "Show inactive", + "title": "Activities", + "successGearEdited": "Gear edited successfully", + "errorGearDelete": "Error deleting gear", + "errorFetchingGears": "Error fetching gears" +} diff --git a/frontend/app/src/i18n/zh-CN/gears/gearsView.json b/frontend/app/src/i18n/zh-CN/gears/gearsView.json new file mode 100644 index 000000000..a3933ff4b --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/gears/gearsView.json @@ -0,0 +1,13 @@ +{ + "title": "Gear", + "buttonAddGear": "Add Gear", + "subTitleSearchGearByNickname": "Search gear by nickname", + "placeholderSearchGearByNickname": "Nickname", + "buttonSearchGear": "Search Gear", + "displayUserNumberOfGears1": "There is a total of ", + "displayUserNumberOfGears2": " gear(s) (", + "displayUserNumberOfGears3": " loaded):", + "successGearDeleted": "Gear deleted successfully", + "errorGearNotFound": "Gear not found", + "errorFetchingGears": "Error fetching gears" +} diff --git a/frontend/app/src/i18n/zh-CN/generalItems.json b/frontend/app/src/i18n/zh-CN/generalItems.json new file mode 100644 index 000000000..c3c5ca41a --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/generalItems.json @@ -0,0 +1,75 @@ +{ + "buttonBack": "Back", + "buttonClose": "Close", + "true": "True", + "false": "False", + "yes": "Yes", + "no": "No", + "ofWithSpaces": " of ", + "languageOption1": "English (US)", + "languageOption2": "Catalan (CA)", + "languageOption3": "Portuguese (PT)", + "languageOption4": "German (DE)", + "languageOption5": "French (FR)", + "languageOption6": "Dutch (NL)", + "languageOption7": "Spanish (ES)", + "firstDayOfWeekOption0": "Sunday", + "firstDayOfWeekOption1": "Monday", + "firstDayOfWeekOption2": "Tuesday", + "firstDayOfWeekOption3": "Wednesday", + "firstDayOfWeekOption4": "Thursday", + "firstDayOfWeekOption5": "Friday", + "firstDayOfWeekOption6": "Saturday", + "buttonlistAll": "List all", + "requiredField": "Required fields", + "labelNotApplicable": "N/A", + "labelNoData": "No data", + "unitsCm": "cm", + "unitsCms": "cms", + "unitsM": "m", + "unitsKm": "km", + "unitsKmH": "km/h", + "unitsKg": "kg", + "labelWeightInKg": "Weight in kg", + "unitsInches": "inches", + "unitsFeet": "feet", + "unitsFeetShort": "ft", + "unitsFeetInches": "feet, inches", + "unitsMiles": "mi", + "unitsYards": "yd", + "unitsMph": "mph", + "unitsLbs": "lbs", + "labelWeightInLbs": "Weight in lbs", + "unitsCalories": "kcal", + "unitsBpm": "bpm", + "labelHRinBpm": "Heart rate in bpm", + "unitsWattsShort": "W", + "labelPowerInWatts": "Power in watts", + "labelCadenceInRpm": "Cadence in rpm", + "unitsSpm": "spm", + "labelElevationInMeters": "Elevation in meters", + "labelElevationInFeet": "Elevation in feet", + "labelVelocityInKmH": "Speed in km/h", + "labelVelocityInMph": "Speed in mph", + "labelPaceInMinKm": "Pace in min/km", + "labelPaceInMin100m": "Pace in min/100m", + "labelPaceInMinMile": "Pace in min/mile", + "labelPaceInMin100yd": "Pace in min/100yd", + "labelLaps": "Laps", + "labelRest": "Rest", + "labelStrokeRateInSpm": "Stroke rate in spm", + "startDateLabel": "Start date", + "endDateLabel": "End date", + "cancel": "Cancel", + "loading": "Loading", + "betaTag": " (Beta)", + "currencyEuro": "Euro", + "currencyEuroSymbol": "€", + "currencyDollar": "US Dollar", + "currencyDollarSymbol": "$", + "currencyPound": "British Pound", + "currencyPoundSymbol": "£", + "genderMale": "Male", + "genderFemale": "Female", + "genderUnspecified": "Unspecified" +} diff --git a/frontend/app/src/i18n/zh-CN/healthView.json b/frontend/app/src/i18n/zh-CN/healthView.json new file mode 100644 index 000000000..49e8a26e8 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/healthView.json @@ -0,0 +1,5 @@ +{ + "title": "Health", + "errorFetchingHealthData": "Error fetching health data", + "errorFetchingHealthTargets": "Error fetching health targets" +} diff --git a/frontend/app/src/i18n/zh-CN/homeView.json b/frontend/app/src/i18n/zh-CN/homeView.json new file mode 100644 index 000000000..d8b2d27bb --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/homeView.json @@ -0,0 +1,18 @@ +{ + "title": "Endurain", + "buttonAddActivity": "Add Activity", + "fieldLabelUploadFileType": "Upload .gpx, .fit, .tcx or .gz file", + "radioUserActivities": "My activities", + "radioFollowerActivities": "Followers activities", + "pillIsHidden": "Hidden", + "successActivityAdded": "Activity added successfully", + "errorActivityAdded": "Error adding activity", + "refreshingActivities": "Refreshing activities from linked services", + "successActivitiesRefreshed": "Activities refreshed successfully", + "errorActivityNotFound": "Activity not found", + "processingActivity": "Processing activity", + "successActivityDeleted": "Activity deleted successfully", + "errorFetchingUserStats": "Error fetching user stats", + "errorFetchingUserActivities": "Error fetching user activities", + "errorFetchingMedia": "Error fetching media for activity" +} diff --git a/frontend/app/src/i18n/zh-CN/loginView.json b/frontend/app/src/i18n/zh-CN/loginView.json new file mode 100644 index 000000000..6f39d8899 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/loginView.json @@ -0,0 +1,37 @@ +{ + "sessionExpired": "User session expired", + "logoutSuccess": "You have been successfully logged out", + "error401": "Invalid username or password", + "error403": "You do not have permission to access this resource", + "error500": "It was not possible to connect to the server. Please try again later", + "errorUndefined": "It was not possible to connect to the server. Please try again later", + "subtitle": "Sign-in below", + "username": "Username", + "password": "Password", + "mfaCode": "MFA Code", + "mfaRequired": "Multi-factor authentication required. Please enter your 6-digit code.", + "verifyMFAButton": "Verify MFA", + "invalidMFACode": "Invalid MFA code. Please try again.", + "neverExpires": "Remember me (do not tick this box if you are using a shared computer)", + "signInButton": "Sign in", + "signUpText": "Looking for signing up?", + "signUpButton": "Sign up", + "errorPublicActivityNotFound": "Public activity not found", + "errorPublic_shareable_links": "Public shareable links are not allowed. To view this activity, you must be signed in", + "forgotPassword": "Forgot your password?", + "passwordResetInvalidLink": "No password reset token provided", + "passwordResetSuccess": "Your password has been reset successfully", + "forgotPasswordModalTitle": "Forgot Password", + "forgotPasswordModalEmailLabel": "Email address", + "forgotPasswordModalEmailHelp": "Enter the email address associated with your account. An email with a link to reset your password will be sent.", + "forgotPasswordModalSubmitButton": "Send Reset Link", + "forgotPasswordModalEmailRequired": "Email address is required", + "forgotPasswordModalRequestSuccess": "If the email exists in the system, a password reset link will be sent to the provided email address", + "forgotPasswordModalRequestError": "Failed to process password reset request", + "forgotPasswordModalEmailNotConfigured": "Email service is not configured. Please contact the administrator", + "forgotPasswordModalUnableToSendEmail": "Unable to send email. Please try again later or contact the administrator", + "signUpLink": "Don't have an account? Sign up", + "emailVerificationSent": "Please check your email for verification instructions", + "adminApprovalRequired": "Your account is pending admin approval", + "verifyEmailInvalidLink": "Invalid email verification link" +} diff --git a/frontend/app/src/i18n/zh-CN/notFoundView.json b/frontend/app/src/i18n/zh-CN/notFoundView.json new file mode 100644 index 000000000..e62ef72f5 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/notFoundView.json @@ -0,0 +1,5 @@ +{ + "title": "Oops! Page not found", + "subTitle": "The page you are looking for does not exist or it was changed.", + "backToHomeButton": "Back to home" +} diff --git a/frontend/app/src/i18n/zh-CN/resetPassword.json b/frontend/app/src/i18n/zh-CN/resetPassword.json new file mode 100644 index 000000000..59d812b06 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/resetPassword.json @@ -0,0 +1,11 @@ +{ + "title": "Reset Password", + "newPasswordLabel": "New Password", + "confirmPasswordLabel": "Confirm New Password", + "submitButton": "Reset Password", + "backToLogin": "Back to Login", + "passwordComplexityError": "Password must be at least 8 characters long, include an uppercase letter, a number, and a special character", + "passwordMismatchError": "Passwords do not match", + "resetError": "Failed to reset password", + "invalidOrExpiredToken": "Invalid or expired password reset token" +} diff --git a/frontend/app/src/i18n/zh-CN/searchView.json b/frontend/app/src/i18n/zh-CN/searchView.json new file mode 100644 index 000000000..a3f96789f --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/searchView.json @@ -0,0 +1,37 @@ +{ + "searchSelectLabel": "Search", + "searchSelectOptionActivity": "Activity", + "searchSelectOptionUser": "User", + "searchSelectOptionGear": "Gear", + "searchSelectActivityType0": "All", + "searchSelectActivityType1": "Run", + "searchSelectActivityType2": "Ride", + "searchSelectActivityType3": "Swim", + "searchSelectActivityType4": "Workout", + "searchSelectActivityType5": "Walk", + "searchSelectActivityType6": "Hike", + "searchSelectActivityType7": "Row", + "searchSelectActivityType8": "Yoga", + "searchSelectActivityType9": "Ski", + "searchSelectActivityType10": "Snowboard", + "searchSelectActivityType11": "Racquet sports", + "searchSelectActivityType12": "Windsurf", + "searchSelectActivityType13": "Stand up paddling", + "searchSelectActivityType14": "Surf", + "searchSelectActivityType15": "Ice skate", + "searchSelectActivityType16": "Soccer", + "searchSelectGearType0": "All", + "searchSelectGearType1": "Bike", + "searchSelectGearType2": "Shoes", + "searchSelectGearType3": "Wetsuit", + "searchSelectGearType4": "Racquet", + "searchSelectGearType5": "Skis", + "searchSelectGearType6": "Snowboard", + "searchSelectGearType7": "Windsurf", + "searchSelectGearType8": "Water sports board", + "resultIsInactiveBadge": "Inactive", + "searchInputPlaceholder": "Search text", + "errorFetchingUserWithUsernameContains": "Error fetching user with username contains logic", + "errorFetchingActivityWithNameContains": "Error fetching activity with name contains logic", + "errorFetchingGearWithNicknameContains": "Error fetching gear with nickname contains logic" +} diff --git a/frontend/app/src/i18n/zh-CN/settingsView.json b/frontend/app/src/i18n/zh-CN/settingsView.json new file mode 100644 index 000000000..1ed130b5c --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/settingsView.json @@ -0,0 +1,3 @@ +{ + "title": "Settings" +} diff --git a/frontend/app/src/i18n/zh-CN/signupView.json b/frontend/app/src/i18n/zh-CN/signupView.json new file mode 100644 index 000000000..bbfed0d78 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/signupView.json @@ -0,0 +1,31 @@ +{ + "subtitle": "Create your account below", + "name": "Full Name", + "username": "Username", + "email": "Email Address", + "password": "Password", + "preferredLanguage": "Preferred Language", + "city": "City", + "birthdate": "Birth Date", + "gender": "Gender", + "units": "Units", + "metric": "Metric", + "imperial": "Imperial", + "height": "Height", + "firstDayOfWeek": "First Day of Week", + "currency": "Currency", + "signUpButton": "Create Account", + "alreadyHaveAccount": "Already have an account? Sign in", + "success": "Sign-up successful", + "errorNameRequired": "Full name is required", + "errorUsernameRequired": "Username is required", + "errorEmailRequired": "Email address is required", + "errorEmailInvalid": "Please enter a valid email address", + "errorPasswordRequired": "Password is required", + "errorPasswordTooShort": "Password must be at least 8 characters long", + "errorUserExists": "A user with this email or username already exists", + "errorSignupDisabled": "Sign-up is not enabled on this server", + "errorValidation": "Please check your input and try again", + "errorGeneral": "An error occurred during sign-up", + "signupDisabled": "User sign-up is not enabled on this server" +} \ No newline at end of file diff --git a/frontend/app/src/i18n/zh-CN/strava/stravaCallbackView.json b/frontend/app/src/i18n/zh-CN/strava/stravaCallbackView.json new file mode 100644 index 000000000..aec6e5891 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/strava/stravaCallbackView.json @@ -0,0 +1,4 @@ +{ + "stravaCallbackViewTitle1": "Handling Strava callback", + "stravaCallbackViewTitle2": "Please wait while Strava is being linked to your account. Do not refresh this page." +} diff --git a/frontend/app/src/i18n/zh-CN/summaryView.json b/frontend/app/src/i18n/zh-CN/summaryView.json new file mode 100644 index 000000000..afc91a642 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/summaryView.json @@ -0,0 +1,44 @@ +{ + "title": "Activity summary", + "filterLabelActivityType": "Type", + "filterOptionAllTypes": "All types", + "labelViewType": "View by", + "optionDaily": "Daily", + "optionWeekly": "Weekly", + "optionMonthly": "Monthly", + "optionYearly": "Yearly", + "optionLifetime": "Lifetime", + "labelSelectWeek": "Week", + "labelSelectMonth": "Month", + "labelSelectYear": "Year", + "labelSelectPeriod": "Period", + "buttonPreviousPeriod": "Previous", + "buttonNextPeriod": "Next", + "headerSummaryFor": "Summary for {period}", + "headerBreakdown": "Breakdown", + "headerActivitiesInPeriod": "Activities in period", + "errorLoadingActivityTypes": "Error loading activity types", + "errorLoadingSummary": "Error loading summary", + "errorLoadingSummaryLoad": "Error loading summary on page load", + "errorFetchingActivities": "Error fetching activities", + "noDataForPeriod": "No data for this period.", + "colDay": "Day", + "colWeekNum": "Week #", + "colMonth": "Month", + "colDistance": "Distance", + "colDuration": "Duration", + "colElevation": "Elevation", + "colCalories": "Calories", + "colActivities": "Activities", + "metricTotalDistance": "Total distance", + "metricTotalDuration": "Total duration", + "metricTotalElevation": "Total elevation", + "metricTotalCalories": "Total calories", + "metricTotalActivities": "Total activities", + "invalidYearSelected": "Invalid year selected", + "headerTypeBreakdown": "Breakdown by type", + "colActivityType": "Type", + "headerYear": "Year {year}", + "headerWeekStarting": "Week of {date}", + "colYear": "Year" +} diff --git a/frontend/app/src/i18n/zh-CN/userView.json b/frontend/app/src/i18n/zh-CN/userView.json new file mode 100644 index 000000000..4511e1da2 --- /dev/null +++ b/frontend/app/src/i18n/zh-CN/userView.json @@ -0,0 +1,32 @@ +{ + "thisMonthActivitiesNumber": "This month activities", + "userFollowing": "Following", + "userFollowers": "Followers", + "navigationActivities": "Activities", + "navigationFollowing": "Following", + "navigationFollowers": "Followers", + "navigationUserSettings": "User settings", + "navigationFollow": "Follow", + "modalFollowUserTitle": "Follow user", + "modalFollowUserBody": "Are you sure you want to follow user ", + "errorUnableToSendFollow": "Unable to send follow request to user", + "successFollowRequestSent": "Follow request sent", + "navigationRequestSent": "Request sent", + "modalCancelFollowRequestTitle": "Cancel follow request", + "modalCancelFollowRequestBody": "Are you sure you want to cancel follow request for user ", + "errorUnableToCancelFollowRequest": "Unable to cancel follow request for user", + "successFollowRequestCancelled": "Follow request cancelled", + "navigationUnfollow": "Unfollow", + "modalUnfollowUserTitle": "Unfollow user", + "modalUnfollowUserBody": "Are you sure you want to unfollow user ", + "errorUnableToUnfollow": "Unable to unfollow user", + "successUserUnfollowed": "User unfollowed", + "activitiesPaginationWeek0": "This week", + "activitiesPaginationWeek51": "One year ago", + "successFollowingDeleted": "Following deleted", + "successFollowerDeleted": "Follower deleted", + "successFollowerAccepted": "Follower accepted", + "errorFetchingUserStats": "Error fetching user stats", + "errorFetchingUserFollowers": "Error fetching user followers", + "errorFetchingUserActivities": "Error fetching user activities" +} diff --git a/frontend/app/src/i18n/zh-TW/activitiesView.json b/frontend/app/src/i18n/zh-TW/activitiesView.json new file mode 100644 index 000000000..3ca38492b --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/activitiesView.json @@ -0,0 +1,14 @@ +{ + "title": "Activities", + "filterLabelType": "Type", + "filterOptionAllTypes": "All types", + "filterLabelFromDate": "From date", + "filterLabelToDate": "To date", + "filterLabelNameLocation": "Name or location", + "filterPlaceholderNameLocation": "e.g., Morning run", + "buttonClear": "Clear", + "buttonApply": "Apply", + "errorFailedFetchActivityTypes": "Failed to fetch activity types", + "errorUpdatingActivities": "Failed to updated activities", + "errorFetchingActivities": "Failed to fetch user activities" +} diff --git a/frontend/app/src/i18n/zh-TW/activityItems.json b/frontend/app/src/i18n/zh-TW/activityItems.json new file mode 100644 index 000000000..0bf315638 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/activityItems.json @@ -0,0 +1,42 @@ +{ + "run": "Run", + "trailRun": "Trail run", + "virtualRun": "Virtual run", + "ride": "Ride", + "gravelRide": "Gravel ride", + "mtbRide": "MTB ride", + "virtualRide": "Virtual ride", + "lapSwimming": "Lap swimming", + "openWaterSwimming": "Open water swimming", + "workout": "Workout", + "walk": "Walk", + "indoorWalk": "Indoor walk", + "hike": "Hike", + "rowing": "Rowing", + "yoga": "Yoga", + "alpineSki": "Alpine ski", + "nordicSki": "Nordic ski", + "snowboard": "Snowboard", + "transition": "Transition", + "strengthTraining": "Strength training", + "crossfit": "CrossFit", + "tennis": "Tennis", + "tableTennis": "Table tennis", + "badminton": "Badminton", + "squash": "Squash", + "racquetball": "Racquetball", + "pickleball": "Pickleball", + "commutingRide": "Commuting ride", + "indoorRide": "Indoor ride", + "mixedSurfaceRide": "Mixed surface ride", + "windsurf": "Windsurf", + "standUpPaddling": "Stand up paddling", + "surf": "Surf", + "trackRun": "Track run", + "ebikeRide": "E-Bike ride", + "ebikeMountainRide": "E-Bike mountain ride", + "iceSkate": "Ice skate", + "soccer": "Soccer", + "padel": "Padel", + "labelWorkout": " workout" +} diff --git a/frontend/app/src/i18n/zh-TW/activityView.json b/frontend/app/src/i18n/zh-TW/activityView.json new file mode 100644 index 000000000..1def65a6a --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/activityView.json @@ -0,0 +1,13 @@ +{ + "labelGear": "Gear", + "labelGearNotSet": "Not set", + "modalLabelDeleteGear": "Delete gear from activity", + "modalLabelDeleteGearBody": "Are you sure you want to remove the gear from the activity?", + "modalLabelDeleteGearButton": "Delete gear", + "successMessageGearAdded": "Gear added to activity", + "successMessageGearDeleted": "Gear deleted from activity", + "errorMessageDeleteGear": "Error deleting gear from activity", + "errorMessageActivityNotFound": "Activity not found", + "alertPrivacyMessage": "You have hidden information in this activity. You can see it, but others cannot.", + "isHiddenMessage": "This activity is hidden. Probably because it is a duplicate or was hidden by the user." +} diff --git a/frontend/app/src/i18n/zh-TW/components/activities/activitiesTableComponent.json b/frontend/app/src/i18n/zh-TW/components/activities/activitiesTableComponent.json new file mode 100644 index 000000000..52f419c13 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/activities/activitiesTableComponent.json @@ -0,0 +1,12 @@ +{ + "headerType": "Type", + "headerName": "Name", + "headerLocation": "Location", + "headerStartTime": "Start time", + "headerDuration": "Duration", + "headerDistance": "Distance", + "headerPace": "Pace/Speed", + "headerCalories": "Calories", + "headerElevation": "Elevation", + "headerAvgHr": "Avg HR" +} diff --git a/frontend/app/src/i18n/zh-TW/components/activities/activityBellowMPillsComponent.json b/frontend/app/src/i18n/zh-TW/components/activities/activityBellowMPillsComponent.json new file mode 100644 index 000000000..00a22da7c --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/activities/activityBellowMPillsComponent.json @@ -0,0 +1,25 @@ +{ + "subTitlePace": "Pace", + "labelAvgPace": "Avg pace", + "labelMovingTime": "Moving time", + "labelElapsedTime": "Elapsed time", + "subTitleSpeed": "Speed", + "labelAvgSpeed": "Avg speed", + "labelMaxSpeed": "Max speed", + "subTitleHeartRate": "Heart rate", + "labelAvgHeartRate": "Avg heart rate", + "labelMaxHeartRate": "Max heart rate", + "subTitlePower": "Power", + "labelAvgPower": "Avg power", + "labelMaxPower": "Max power", + "labelNormalizedPower": "Normalized power", + "subTitleCadence": "Cadence", + "labelAvgCadence": "Avg cadence", + "labelMaxCadence": "Max cadence", + "subTitleElevation": "Elevation", + "labelElevationGain": "Elevation gain", + "labelElevationLoss": "Elevation loss", + "subTitleStrokeRate": "Stroke rate", + "labelAvgStrokeRate": "Avg stroke rate", + "labelMaxStrokeRate": "Max stroke rate" +} diff --git a/frontend/app/src/i18n/zh-TW/components/activities/activityLapsComponent.json b/frontend/app/src/i18n/zh-TW/components/activities/activityLapsComponent.json new file mode 100644 index 000000000..4318c6001 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/activities/activityLapsComponent.json @@ -0,0 +1,14 @@ +{ + "labelLapNumber": "Lap", + "labelLapIntensity": "Intensity", + "labelLapDistance": "Distance", + "labelLapTime": "Time", + "labelLapPace": "Pace", + "labelLapSpeed": "Speed", + "labelLapElevation": "Elevation", + "labelLapElev": "Elev", + "labelLapAvgHr": "Avg heart rate", + "labelLapHR": "HR", + "labelLapStrokeRate": "Stroke rate", + "labelLapSR": "SR" +} diff --git a/frontend/app/src/i18n/zh-TW/components/activities/activityMandAbovePillsComponent.json b/frontend/app/src/i18n/zh-TW/components/activities/activityMandAbovePillsComponent.json new file mode 100644 index 000000000..e04807141 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/activities/activityMandAbovePillsComponent.json @@ -0,0 +1,17 @@ +{ + "labelPillGraphs": "Graphs", + "labelPillLaps": "Laps", + "labelPillWorkoutSets": "Sets", + "labelGraph": "Activity data graphs", + "labelGraphHR": "Heart rate", + "labelHRZones": "Heart rate zones", + "labelGraphPower": "Power", + "labelGraphCadence": "Cadence", + "labelGraphElevation": "Elevation", + "labelGraphVelocity": "Speed", + "labelGraphPace": "Pace", + "labelGraphHRZone": "Zone", + "labelDownsampling": "Data downsampled to ~200 points", + "errorMessageProcessingActivityStreams": "Error processing activity streams", + "labelGraphStrokeRate": "Stroke rate" +} diff --git a/frontend/app/src/i18n/zh-TW/components/activities/activityMapComponent.json b/frontend/app/src/i18n/zh-TW/components/activities/activityMapComponent.json new file mode 100644 index 000000000..330d3e8ed --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/activities/activityMapComponent.json @@ -0,0 +1,8 @@ +{ + "modalMediaDeleteTitle": "Delete media", + "modalMediaDeleteBody1": "Are you sure you want to delete media with ID ", + "modalMediaDeleteBody2": " and name ", + "errorFetchingActivityStream": "Error fetching activity stream data", + "mediaDeletedSuccessfully": "Media deleted successfully", + "errorDeletingMedia": "Error deleting media" +} diff --git a/frontend/app/src/i18n/zh-TW/components/activities/activitySummaryComponent.json b/frontend/app/src/i18n/zh-TW/components/activities/activitySummaryComponent.json new file mode 100644 index 000000000..376d9bc63 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/activities/activitySummaryComponent.json @@ -0,0 +1,31 @@ +{ + "userNameHidden": "Hidden", + "visibilityPublic": "Public", + "visibilityFollowers": "Followers", + "visibilityPrivate": "Private", + "buttonDeleteActivity": "Delete activity", + "buttonEditActivity": "Edit activity", + "buttonAddActivityMedia": "Add media", + "modalDeleteBody1": "Are you sure you want to delete activity ", + "modalDeleteBody2": "This action cannot be undone.", + "modalAddMediaTitle": "Add media", + "modalAddMediaBody": "Upload .png, .jpg or .jpeg file", + "processingMediaUpload": "Processing media upload...", + "successMediaUpload": "Media uploaded successfully", + "errorMediaUpload": "Error uploading media", + "labelVirtual": "(Virtual) ", + "privateNotes": "Private notes", + "activityDistance": "Distance", + "activityTime": "Time", + "activityPace": "Pace", + "activityAvgHR": "Avg HR", + "activityMaxHR": "Max HR", + "activityAvgPower": "Avg power", + "activityAvgSpeed": "Avg speed", + "activityEleGain": "Ele gain", + "activityEleLoss": "Ele loss", + "activityCalories": "Calories", + "activityNoData": "No data", + "errorFetchingUserById": "Error fetching user by id", + "errorDeletingActivity": "Error deleting activity" +} diff --git a/frontend/app/src/i18n/zh-TW/components/activities/activityWorkoutStepsComponent.json b/frontend/app/src/i18n/zh-TW/components/activities/activityWorkoutStepsComponent.json new file mode 100644 index 000000000..509907d96 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/activities/activityWorkoutStepsComponent.json @@ -0,0 +1,20 @@ +{ + "labelWorkoutStepType": "Step type", + "labelWorkoutStepTime": "Step time", + "labelWorkoutStepReps": "Step reps", + "labelWorkoutStepIntensity": "Intensity", + "labelWorkoutStepNotes": "Notes", + "labelWorkoutStepExerciseName": "Step name", + "labelWorkoutStepExerciseWeight": "Weight", + "labelWorkoutStepSwimStroke": "Step swim stroke", + "labelWorkoutSetType": "Set type", + "labelWorkoutSetTime": "Set time", + "labelWorkoutSetReps": "Set reps", + "labelWorkoutSetExerciseName": "Set name", + "labelWorkoutSetExerciseWeight": "Set weight", + "labelWorkoutSetTypeMobile": "Type", + "labelWorkoutSetTimeMobile": "Time", + "labelWorkoutSetRepsMobile": "Reps", + "labelWorkoutSetExerciseNameMobile": "Name", + "labelWorkoutSetExerciseWeightMobile": "Weight" +} diff --git a/frontend/app/src/i18n/zh-TW/components/activities/modals/addGearToActivityModalComponent.json b/frontend/app/src/i18n/zh-TW/components/activities/modals/addGearToActivityModalComponent.json new file mode 100644 index 000000000..734c573b5 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/activities/modals/addGearToActivityModalComponent.json @@ -0,0 +1,6 @@ +{ + "modalLabelAddGear": "Add gear to activity", + "modalLabelSelectGear": "Select gear", + "modalButtonAddGear": "Add gear", + "errorEditingGear": "Error editing gear" +} diff --git a/frontend/app/src/i18n/zh-TW/components/activities/modals/editActivityModalComponent.json b/frontend/app/src/i18n/zh-TW/components/activities/modals/editActivityModalComponent.json new file mode 100644 index 000000000..e5f929c94 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/activities/modals/editActivityModalComponent.json @@ -0,0 +1,68 @@ +{ + "modalEditActivityTitle": "Edit activity", + "modalEditActivityDescriptionLabel": "Description", + "modalEditActivityDescriptionPlaceholder": "Description (max 2500 characters)", + "modalEditActivityPrivateNotesLabel": "Private notes", + "modalEditActivityPrivateNotesPlaceholder": "Private notes (max 2500 characters)", + "modalEditActivityNameLabel": "Name", + "modalEditActivityNamePlaceholder": "Name (max 250 characters)", + "modalEditActivityTypeLabel": "Type", + "modalEditActivityTypeOption1": "Run", + "modalEditActivityTypeOption2": "Trail run", + "modalEditActivityTypeOption3": "Virtual run", + "modalEditActivityTypeOption4": "Ride", + "modalEditActivityTypeOption5": "Gravel ride", + "modalEditActivityTypeOption6": "Mountain bike ride", + "modalEditActivityTypeOption7": "Virtual ride", + "modalEditActivityTypeOption8": "Swim", + "modalEditActivityTypeOption9": "Open water swim", + "modalEditActivityTypeOption10": "Workout", + "modalEditActivityTypeOption11": "Walk", + "modalEditActivityTypeOption12": "Hike", + "modalEditActivityTypeOption13": "Rowing", + "modalEditActivityTypeOption14": "Yoga", + "modalEditActivityTypeOption15": "Alpine skiing", + "modalEditActivityTypeOption16": "Nordic skiing", + "modalEditActivityTypeOption17": "Snowboarding", + "modalEditActivityTypeOption18": "Transition", + "modalEditActivityTypeOption19": "Weight training", + "modalEditActivityTypeOption20": "Crossfit", + "modalEditActivityTypeOption21": "Tennis", + "modalEditActivityTypeOption22": "Table tennis", + "modalEditActivityTypeOption23": "Badminton", + "modalEditActivityTypeOption24": "Squash", + "modalEditActivityTypeOption25": "Racquetball", + "modalEditActivityTypeOption26": "Pickleball", + "modalEditActivityTypeOption27": "Commuting ride", + "modalEditActivityTypeOption28": "Indoor ride", + "modalEditActivityTypeOption29": "Mixed surface ride", + "modalEditActivityTypeOption30": "Windsurf", + "modalEditActivityTypeOption31": "Indoor walk", + "modalEditActivityTypeOption32": "Stand up paddling", + "modalEditActivityTypeOption33": "Surf", + "modalEditActivityTypeOption34": "Track run", + "modalEditActivityTypeOption35": "E-Bike ride", + "modalEditActivityTypeOption36": "E-Bike mountain ride", + "modalEditActivityTypeOption37": "Ice skate", + "modalEditActivityTypeOption38": "Soccer", + "modalEditActivityTypeOption39": "Padel", + "modalEditActivityVisibilityLabel": "Visibility", + "modalEditActivityVisibilityOption0": "Public", + "modalEditActivityVisibilityOption1": "Followers", + "modalEditActivityVisibilityOption2": "Private", + "modalEditActivityIsHiddenLabel": "Is hidden", + "modalEditActivityHideStartTimeLabel": "Hide start time", + "modalEditActivityHideLocationLabel": "Hide location", + "modalEditActivityHideMapLabel": "Hide map", + "modalEditActivityHideHrLabel": "Hide heart rate", + "modalEditActivityHidePowerLabel": "Hide power", + "modalEditActivityHideCadenceLabel": "Hide cadence", + "modalEditActivityHideElevationLabel": "Hide elevation", + "modalEditActivityHideSpeedLabel": "Hide speed", + "modalEditActivityHidePaceLabel": "Hide pace", + "modalEditActivityHideLapsLabel": "Hide laps", + "modalEditActivityHideWorkoutSetsStepsLabel": "Hide workout sets/steps", + "modalEditActivityHideGearLabel": "Hide gear", + "successActivityEdit": "Activity edited successfully", + "errorActivityEdit": "Error editing activity" +} diff --git a/frontend/app/src/i18n/zh-TW/components/followers/followersListComponent.json b/frontend/app/src/i18n/zh-TW/components/followers/followersListComponent.json new file mode 100644 index 000000000..24c3b008d --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/followers/followersListComponent.json @@ -0,0 +1,16 @@ +{ + "requestAccepted": "Accepted", + "requestPending": "Request pending", + "followingModalTitle": "Delete following", + "followingModalBody": "Are you sure you want to delete following user ", + "followerModalTitle": "Delete follower", + "followerModalBody": "Are you sure you want to delete follower user ", + "followerAcceptModalTitle": "Accept user request", + "followerAcceptModalBody": "Are you sure you want to accept follow request from user ", + "followerDeclineModalTitle": "Decline user request ", + "followerDeclineModalBody": "Are you sure you want to decline follow request from user ", + "errorDeleteFollowing": "Error deleting following", + "errorDeleteFollower": "Error deleting follower", + "errorUpdateFollower": "Error updating follower", + "errorFetchingFollowersDetails": "Error fetching followers details" +} diff --git a/frontend/app/src/i18n/zh-TW/components/gears/gearComponentAddEditModalComponent.json b/frontend/app/src/i18n/zh-TW/components/gears/gearComponentAddEditModalComponent.json new file mode 100644 index 000000000..328c4f19a --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/gears/gearComponentAddEditModalComponent.json @@ -0,0 +1,19 @@ +{ + "addEditGearComponentModalAddTitle": "Add gear component", + "addEditGearComponentModalEditTitle": "Edit gear component", + "addEditGearComponentModalAddEditTypeLabel": "Type", + "addEditGearComponentModalAddEditBrandLabel": "Brand", + "addEditGearComponentModalAddEditModelLabel": "Model", + "addEditGearComponentModalAddEditPurchaseDateLabel": "Purchase date", + "addEditGearComponentModalAddEditExpectedDistanceLabel": "Expected distance", + "addEditGearComponentModalAddEditExpectedTimeLabel": "Expected time", + "addEditGearComponentModalAddEditPurchaseValueLabel": "Purchase value", + "addEditGearComponentModalAddEditRetiredDateLabel": "Retired date", + "addEditGearComponentModalAddEditIsActiveLabel": "Is active", + "successGearComponentAdded": "Gear component added successfully", + "successGearComponentEdited": "Gear component edited successfully", + "errorGearComponentAdd": "Error adding gear component", + "gearComponentListGearEditSuccessMessage": "Gear component edited successfully", + "gearComponentListGearEditErrorMessage": "Error editing gear component", + "retiredDateAfterPurchaseDateError": "Retired date must be after purchase date" +} diff --git a/frontend/app/src/i18n/zh-TW/components/gears/gearComponentListComponent.json b/frontend/app/src/i18n/zh-TW/components/gears/gearComponentListComponent.json new file mode 100644 index 000000000..802d9093b --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/gears/gearComponentListComponent.json @@ -0,0 +1,79 @@ +{ + "gearComponentBackTire": "Back tire", + "gearComponentFrontTire": "Front tire", + "gearComponentBackTube": "Back tube", + "gearComponentFrontTube": "Front tube", + "gearComponentBackWheelValve": "Back wheel valve", + "gearComponentFrontWheelValve": "Front wheel valve", + "gearComponentBackTubelessSealant": "Back tubeless sealant", + "gearComponentBackTubelessRimTape": "Back tubeless rim tape", + "gearComponentFrontTubelessSealant": "Front tubeless sealant", + "gearComponentFrontTubelessRimTape": "Front tubeless rim tape", + "gearComponentBackWheel": "Back wheel", + "gearComponentFrontWheel": "Front wheel", + "gearComponentBackBreakRotor": "Back brake rotor", + "gearComponentFrontBreakRotor": "Front brake rotor", + "gearComponentBackBreakPads": "Back brake pads", + "gearComponentFrontBreakPads": "Front brake pads", + "gearComponentBackBreakOil": "Back brake oil", + "gearComponentFrontBreakOil": "Front brake oil", + "gearComponentCrankLeftPowerMeter": "Crank left power meter", + "gearComponentCrankRightPowerMeter": "Crank right power meter", + "gearComponentCranksetPowerMeter": "Crankset power meter", + "gearComponentPedalsLeftPowerMeter": "Pedals left power meter", + "gearComponentPedalsRightPowerMeter": "Pedals right power meter", + "gearComponentPedalsPowerMeter": "Pedals power meter", + "gearComponentPedals": "Pedals", + "gearComponentCrankset": "Crankset", + "gearComponentCassette": "Cassette", + "gearComponentChain": "Chain", + "gearComponentFrontShifter": "Front shifter", + "gearComponentFrontDerailleur": "Front derailleur", + "gearComponentRearShifter": "Rear shifter", + "gearComponentRearDerailleur": "Rear derailleur", + "gearComponentBottomBracket": "Bottom bracket", + "gearComponentBottleCage": "Bottle cage", + "gearComponentHandlebar": "Handlebar", + "gearComponentHeadset": "Headset", + "gearComponentComputerMount": "Computer mount", + "gearComponentHandlebarTape": "Handlebar tape", + "gearComponentGrips": "Grips", + "gearComponentStem": "Stem", + "gearComponentSeatpost": "Seatpost", + "gearComponentSaddle": "Saddle", + "gearComponentFork": "Fork", + "gearComponentFrame": "Frame", + "gearComponentCleats": "Cleats", + "gearComponentInsoles": "Insoles", + "gearComponentLaces": "Laces", + "gearComponentBaseGrip": "Base grip", + "gearComponentBumpers": "Bumpers", + "gearComponentGrommets": "Grommets", + "gearComponentOverGrip": "Over grip", + "gearComponentStrings": "Strings", + "gearComponentSail": "Sail", + "gearComponentBoard": "Board", + "gearComponentMast": "Mast", + "gearComponentBoom": "Boom", + "gearComponentMastExtension": "Mast extension", + "gearComponentMastBase": "Mast base", + "gearComponentMastUniversalJoint": "Mast universal joint", + "gearComponentFin": "Fin", + "gearComponentFootstraps": "Footstraps", + "gearComponentHarnessLines": "Harness lines", + "gearComponentRiggingLines": "Rigging lines", + "gearComponentFootpad": "Footpad", + "gearComponentImpactVest": "Impact vest", + "gearComponentLifeguardVest": "Lifeguard vest", + "gearComponentHelmet": "Helmet", + "gearComponentWing": "Wing", + "gearComponentFrontFoil": "Front foil", + "gearComponentStabilizer": "Stabilizer", + "gearComponentFuselage": "Fuselage", + "gearComponentOf": " of ", + "gearComponentListGearComponentIsInactiveBadge": "Inactive", + "gearComponentListModalDeleteGearComponentTitle": "Delete gear component", + "gearComponentListModalDeleteGearComponentBody": "Are you sure you want to delete gear component ", + "gearComponentListGearDeleteSuccessMessage": "Gear component deleted successfully", + "gearComponentListGearDeleteErrorMessage": "Error deleting gear component" +} diff --git a/frontend/app/src/i18n/zh-TW/components/gears/gearsAddEditGearModalComponent.json b/frontend/app/src/i18n/zh-TW/components/gears/gearsAddEditGearModalComponent.json new file mode 100644 index 000000000..c8badac61 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/gears/gearsAddEditGearModalComponent.json @@ -0,0 +1,29 @@ +{ + "addEditGearModalEditTitle": "Edit gear", + "addEditGearModalAddTitle": "Add gear", + "addEditGearModalAddBrandLabel": "Brand", + "addEditGearModalAddModelLabel": "Model", + "addEditGearModalAddNicknameLabel": "Nickname", + "addEditGearModalAddTypeLabel": "Gear type", + "addEditGearModalAddTypeOption1": "Bike", + "addEditGearModalAddTypeOption2": "Shoes", + "addEditGearModalAddTypeOption3": "Wetsuit", + "addEditGearModalAddTypeOption4": "Racquet", + "addEditGearModalAddTypeOption5": "Skis", + "addEditGearModalAddTypeOption6": "Snowboard", + "addEditGearModalAddTypeOption7": "Windsurf", + "addEditGearModalAddTypeOption8": "Water sports board", + "addEditGearModalAddDateLabel": "Created date", + "addEditGearModalAddIsActiveLabel": "Is active", + "addEditGearModalAddIsActiveOption1": "Active", + "addEditGearModalAddIsActiveOption0": "Inactive", + "addEditGearModalAddIsInitialKmsLabel": "Initial kms", + "addEditGearModalAddIsInitialMilesLabel": "Initial miles", + "addEditGearModalAddEditPurchaseValueLabel": "Purchase value", + "errorNicknameAlreadyExistsFeedback": "Nickname already exists", + "errorNotPossibleToGetGearByNickname": "Is was not possible to get gear by nickname for validation", + "successGearAdded": "Gear added successfully", + "errorGearAdd": "Error adding gear", + "successGearEdited": "Gear edited successfully", + "errorGearEdit": "Error editing gear" +} diff --git a/frontend/app/src/i18n/zh-TW/components/gears/gearsListComponent.json b/frontend/app/src/i18n/zh-TW/components/gears/gearsListComponent.json new file mode 100644 index 000000000..d267f4e4c --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/gears/gearsListComponent.json @@ -0,0 +1,16 @@ +{ + "gearListTypeLabel": "Type", + "gearListTypeOption1": "Bike", + "gearListTypeOption2": "Shoes", + "gearListTypeOption3": "Wetsuit", + "gearListTypeOption4": "Racquet", + "gearListTypeOption5": "Skis", + "gearListTypeOption6": "Snowboard", + "gearListTypeOption7": "Windsurf", + "gearListTypeOption8": "Water sports board", + "gearListGearIsInactiveBadge": "Inactive", + "gearListModalDeleteGearTitle": "Delete gear", + "gearListModalDeleteGearBody": "Are you sure you want to delete gear ", + "gearListGearDeleteSuccessMessage": "Gear deleted successfully", + "gearListGearDeleteErrorMessage": "Error deleting gear" +} diff --git a/frontend/app/src/i18n/zh-TW/components/health/healthDashboardZoneComponent.json b/frontend/app/src/i18n/zh-TW/components/health/healthDashboardZoneComponent.json new file mode 100644 index 000000000..62a49e059 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/health/healthDashboardZoneComponent.json @@ -0,0 +1,13 @@ +{ + "weight": "Weight", + "noWeightData": "No weight data", + "noWeightTarget": "No weight target", + "noHeightDefined": "No height defined for user", + "bmi": "BMI", + "bmiUnderweight": "Underweight", + "bmiNormalWeight": "Normal weight", + "bmiOverweight": "Overweight", + "bmiObesityClass1": "Obesity (Class 1)", + "bmiObesityClass2": "Obesity (Class 2)", + "bmiObesityClass3": "Extreme Obesity (Class 3)" +} diff --git a/frontend/app/src/i18n/zh-TW/components/health/healthSideBarComponent.json b/frontend/app/src/i18n/zh-TW/components/health/healthSideBarComponent.json new file mode 100644 index 000000000..82d94300d --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/health/healthSideBarComponent.json @@ -0,0 +1,4 @@ +{ + "dashboardSection": "Dashboard", + "weightSection": "Weight" +} diff --git a/frontend/app/src/i18n/zh-TW/components/health/healthWeightZone/healthWeightAddEditModalComponent.json b/frontend/app/src/i18n/zh-TW/components/health/healthWeightZone/healthWeightAddEditModalComponent.json new file mode 100644 index 000000000..2af10b444 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/health/healthWeightZone/healthWeightAddEditModalComponent.json @@ -0,0 +1,8 @@ +{ + "addWeightModalTitle": "Add weight", + "editWeightModalTitle": "Edit weight", + "addWeightWeightLabel": "Weight", + "addWeightDateLabel": "Date", + "successAddWeight": "Weight added", + "errorAddWeight": "Error adding weight" +} diff --git a/frontend/app/src/i18n/zh-TW/components/health/healthWeightZone/healthWeightListComponent.json b/frontend/app/src/i18n/zh-TW/components/health/healthWeightZone/healthWeightListComponent.json new file mode 100644 index 000000000..5d457e51f --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/health/healthWeightZone/healthWeightListComponent.json @@ -0,0 +1,9 @@ +{ + "labelGarminConnect": "Garmin Connect", + "modalDeleteWeightTitle": "Delete weight", + "modalDeleteWeightBody": "Are you sure you want to delete weight entry for ", + "successDeleteWeight": "Weight deleted", + "errorDeleteWeight": "It was not possible to delete weight entry", + "successEditWeight": "Weight edited", + "errorEditWeight": "It was not possible to edit weight entry" +} diff --git a/frontend/app/src/i18n/zh-TW/components/health/healthWeightZoneComponent.json b/frontend/app/src/i18n/zh-TW/components/health/healthWeightZoneComponent.json new file mode 100644 index 000000000..8bcd11daa --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/health/healthWeightZoneComponent.json @@ -0,0 +1,6 @@ +{ + "buttonAddWeight": "Add weight", + "labelNumberOfHealthDataWeight1": "There is a total of ", + "labelNumberOfHealthDataWeight2": " weight measure(s) inserted (", + "labelNumberOfHealthDataWeight3": " loaded):" +} diff --git a/frontend/app/src/i18n/zh-TW/components/navbar/navbarBottomMobileComponent.json b/frontend/app/src/i18n/zh-TW/components/navbar/navbarBottomMobileComponent.json new file mode 100644 index 000000000..d164dd958 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/navbar/navbarBottomMobileComponent.json @@ -0,0 +1,7 @@ +{ + "home": "Home", + "gear": "Gear", + "health": "Health", + "alerts": "Alerts", + "menu": "Menu" +} diff --git a/frontend/app/src/i18n/zh-TW/components/navbar/navbarComponent.json b/frontend/app/src/i18n/zh-TW/components/navbar/navbarComponent.json new file mode 100644 index 000000000..487ba2305 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/navbar/navbarComponent.json @@ -0,0 +1,13 @@ +{ + "search": "Search", + "activities": "Activities", + "activitiesList": "List", + "summary": "Summary", + "gear": "Gear", + "health": "Health", + "profile": "Profile", + "settings": "Settings", + "login": "Login", + "logout": "Logout", + "errorLogout": "Error logging out" +} diff --git a/frontend/app/src/i18n/zh-TW/components/noItemsFoundComponent.json b/frontend/app/src/i18n/zh-TW/components/noItemsFoundComponent.json new file mode 100644 index 000000000..8daaee206 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/noItemsFoundComponent.json @@ -0,0 +1,4 @@ +{ + "title": "Ops...", + "subtitle": "No records found" +} diff --git a/frontend/app/src/i18n/zh-TW/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/zh-TW/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json new file mode 100644 index 000000000..5a675a94b --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -0,0 +1,4 @@ +{ + "title": "New sign-up request", + "subTitle": " has requested to sign-up" +} \ No newline at end of file diff --git a/frontend/app/src/i18n/zh-TW/components/notifications/navbarNotificationsComponent.json b/frontend/app/src/i18n/zh-TW/components/notifications/navbarNotificationsComponent.json new file mode 100644 index 000000000..8fdb7cb7d --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/notifications/navbarNotificationsComponent.json @@ -0,0 +1,6 @@ +{ + "errorFetchingNotificationsPagination": "Error fetching notifications with pagination", + "errorFetchingNotificationsNumber": "Error fetching notifications number", + "errorFetchingNotificationById": "Error fetching notification by ID", + "errorFetchingMessageFromWebSocket": "Error fetching message from WebSocket" +} diff --git a/frontend/app/src/i18n/zh-TW/components/notifications/newAcceptedRequestNotificationComponent.json b/frontend/app/src/i18n/zh-TW/components/notifications/newAcceptedRequestNotificationComponent.json new file mode 100644 index 000000000..0cd86fce6 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/notifications/newAcceptedRequestNotificationComponent.json @@ -0,0 +1,4 @@ +{ + "newAcceptedRequestTitle": "New accepted request", + "newAcceptedRequestSubTitle": " has accepted your follow request" +} diff --git a/frontend/app/src/i18n/zh-TW/components/notifications/newActivityDuplicateStartTimeNotificationComponent.json b/frontend/app/src/i18n/zh-TW/components/notifications/newActivityDuplicateStartTimeNotificationComponent.json new file mode 100644 index 000000000..be308a048 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/notifications/newActivityDuplicateStartTimeNotificationComponent.json @@ -0,0 +1,4 @@ +{ + "newActivityDuplicateStartTimeTitle": "New activity with duplicated start time", + "newActivityDuplicateStartTimeSubTitle": "A new activity has been added with a start time that overlaps with an existing activity. Please review it" +} diff --git a/frontend/app/src/i18n/zh-TW/components/notifications/newActivityNotificationComponent.json b/frontend/app/src/i18n/zh-TW/components/notifications/newActivityNotificationComponent.json new file mode 100644 index 000000000..3b5599c3a --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/notifications/newActivityNotificationComponent.json @@ -0,0 +1,4 @@ +{ + "newActivityTitle": "New activity", + "newActivitySubTitle": "Good job! A new activity has been added!" +} diff --git a/frontend/app/src/i18n/zh-TW/components/notifications/newFollowerRequestNotificationComponent.json b/frontend/app/src/i18n/zh-TW/components/notifications/newFollowerRequestNotificationComponent.json new file mode 100644 index 000000000..9d34f427b --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/notifications/newFollowerRequestNotificationComponent.json @@ -0,0 +1,4 @@ +{ + "newFollowerRequestTitle": "New follower request", + "newFollowerRequestSubTitle": "You have a new follower request from " +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsGeneralZone/settingsLanguageSwitcherComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsGeneralZone/settingsLanguageSwitcherComponent.json new file mode 100644 index 000000000..334897cd8 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsGeneralZone/settingsLanguageSwitcherComponent.json @@ -0,0 +1,3 @@ +{ + "formLabel": "Language" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsGeneralZone/settingsThemeSwitcherComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsGeneralZone/settingsThemeSwitcherComponent.json new file mode 100644 index 000000000..02516aca0 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsGeneralZone/settingsThemeSwitcherComponent.json @@ -0,0 +1,6 @@ +{ + "formLabel": "Theme", + "themeLight": "Light", + "themeDark": "Dark", + "themeAuto": "Auto" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsImportZoneComponent.json new file mode 100644 index 000000000..0fe6a9781 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsImportZoneComponent.json @@ -0,0 +1,13 @@ +{ + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", + "buttonBulkImport": "Import activities", + "loadingMessageBulkImport": "Importing activities from files...", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaGearImportTitle": "Strava gear import", + "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", + "stravaGearImportbuttonBikes": "Import Strava bikes", + "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", + "successMessageStravaBikesImport": "Strava bikes imported successfully", + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsIntegrationsZone/garminConnectLoginModalComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsIntegrationsZone/garminConnectLoginModalComponent.json new file mode 100644 index 000000000..01f90a110 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsIntegrationsZone/garminConnectLoginModalComponent.json @@ -0,0 +1,14 @@ +{ + "garminConnectAuthModalTitle": "Link Garmin Connect account", + "garminConnectAuthModalUsernameLabel": "Garmin Connect email", + "garminConnectAuthModalUsernamePlaceholder": "Garmin Connect email", + "garminConnectAuthModalPasswordLabel": "Garmin Connect password", + "garminConnectAuthModalPasswordPlaceholder": "Garmin Connect password", + "garminConnectAuthModalMfaCodeLabel": "MFA code", + "garminConnectAuthModalMfaCodePlaceholder": "MFA code", + "buttonSubmitMfaCode": "Submit MFA code", + "garminConnectAuthModalLoginButton": "Login", + "processingMessageLinkGarminConnect": "Linking Garmin Connect account...", + "successMessageLinkGarminConnect": "Garmin Connect account linked", + "errorMessageUnableToLinkGarminConnect": "Unable to link Garmin Connect account" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsIntegrationsZoneComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsIntegrationsZoneComponent.json new file mode 100644 index 000000000..ab40baa88 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsIntegrationsZoneComponent.json @@ -0,0 +1,47 @@ +{ + "stravaIntegrationTitle": "Strava", + "stravaIntegrationBody": "Strava is an American internet service for tracking physical exercise which incorporates social network features.", + "buttonConnect": "Connect", + "buttonDropdownOptions": "Options", + "modalRetrieveActivitiesByDaysTitle": "Retrieve activities by days", + "modalRetrieveActivitiesByDateRangeTitle": "Retrieve activities by date range", + "modalRetrieveActivitiesByDaysLabel": "Days", + "modalRetrieveActivitiesByDaysPlaceholder": "Days", + "modalRetrieveButton": "Retrieve", + "buttonRetrieveGear": "Retrieve gear", + "buttonRelink": "Relink", + "buttonUnlink": "Unlink", + "modalRetrieveClientIdTitle": "Connect Strava", + "modalRetrieveClientIdLabel": "Client ID", + "modalRetrieveClientSecretLabel": "Client secret", + "errorMessageUnableToLinkStrava": "Unable to link Strava account", + "errorMessageUnableToUnsetStravaClientSettings": "Unable to unset Strava client and state settings after linking error", + "successMessageStravaAccountLinked": "Strava account linked", + "errorMessageUnableToUnSetStravaState": "Unable to unset Strava state", + "errorMessageUnableToGetStravaActivities": "Unable to get Strava activities", + "errorMessageUnableToGetStravaGear": "Unable to get Strava gear", + "loadingMessageRetrievingStravaActivities": "Retrieving Strava activities", + "loadingMessageRetrievingStravaGear": "Retrieving Strava gear", + "processingMessageUnlinkStrava": "Unlinking Strava account...", + "successMessageStravaUnlinked": "Strava account unlinked", + "errorMessageUnableToUnlinkStrava": "Unable to unlink Strava account", + "modalUnlinkStravaTitle": "Unlink Strava", + "modalUnlinkStravaBody": "Are you sure you want to unlink your Strava account? Unlinking your Strava account will remove all your Strava activities and gear from Endurain.", + "garminConnectIntegrationTitle": "Garmin Connect", + "garminConnectIntegrationBody": "Garmin Connect is a health and fitness activity platform for users of Garmin devices", + "loadingMessageRetrievingGarminConnectActivities": "Retrieving Garmin Connect activities", + "errorMessageUnableToGetGarminConnectActivitiesDays": "Unable to get Garmin Connect activities by days", + "errorMessageUnableToGetGarminConnectActivitiesDataRange": "Unable to get Garmin Connect activities using data range", + "modalUnlinkGarminConnectTitle": "Unlink Garmin Connect", + "modalUnlinkGarminConnectBody": "Are you sure you want to unlink your Garmin Connect account?", + "processingMessageUnlinkGarminConnect": "Unlinking Garmin Connect account...", + "successMessageGarminConnectUnlinked": "Garmin Connect account unlinked", + "errorMessageUnableToUnlinkGarminConnect": "Unable to unlink Garmin Connect account", + "errorMessageUnableToGetGarminConnectGear": "Unable to get Garmin Connect gear", + "loadingMessageRetrievingGarminConnectGear": "Retrieving Garmin Connect gear", + "modalRetrieveHealthDataByDaysTitle": "Retrieve health data by days", + "modalRetrieveHealthDataByDateRangeTitle": "Retrieve health data by date range", + "errorMessageUnableToGetGarminConnectHealthDataDays": "Unable to get Garmin Connect health data by days", + "errorMessageUnableToGetGarminConnectHealthDataDateRange": "Unable to get Garmin Connect health data using date range", + "loadingMessageRetrievingGarminConnectHealthData": "Retrieving Garmin Connect health data" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsSecurityZoneComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsSecurityZoneComponent.json new file mode 100644 index 000000000..cab71a900 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsSecurityZoneComponent.json @@ -0,0 +1,31 @@ +{ + "subtitleChangePassword": "Change password", + "changeUserPasswordBodyLabel": "Change password for user ", + "changeUserPasswordPasswordLabel": "New password", + "changeUserPasswordPasswordConfirmationLabel": "Confirm new password", + "changeUserPasswordFeedbackLabel": "Password does not meet requirements", + "changeUserPasswordPasswordsDoNotMatchFeedbackLabel": "Passwords do not match", + "subtitleMFA": "Multi-Factor Authentication (MFA)", + "mfaDisabledDescription": "MFA is currently disabled. Enable it to add an extra layer of security to your account.", + "mfaEnabledDescription": "MFA is currently enabled. Your account is protected with two-factor authentication.", + "enableMFAButton": "Enable MFA", + "disableMFAButton": "Disable MFA", + "mfaSetupModalTitle": "Setup Multi-Factor Authentication", + "mfaSetupInstructions": "Scan the QR code below with your authenticator app (Google Authenticator, Authy, etc.) or manually enter the secret:", + "mfaSecretLabel": "Secret Key", + "mfaVerificationCodeLabel": "Verification Code", + "mfaVerificationCodePlaceholder": "Enter 6-digit code", + "mfaDisableModalTitle": "Disable Multi-Factor Authentication", + "mfaDisableConfirmation": "Are you sure you want to disable MFA? This will reduce your account security.", + "mfaEnabledSuccess": "MFA enabled successfully", + "mfaDisabledSuccess": "MFA disabled successfully", + "errorLoadMFAStatus": "Error loading MFA status", + "errorSetupMFA": "Error setting up MFA", + "errorEnableMFA": "Error enabling MFA", + "errorDisableMFA": "Error disabling MFA", + "subtitleMySessions": "My sessions", + "userChangePasswordSuccessMessage": "Password changed successfully", + "userChangePasswordErrorMessage": "Error changing password", + "successDeleteSession": "Session deleted successfully", + "errorDeleteSession": "Error deleting session" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsServerSettingsZoneComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsServerSettingsZoneComponent.json new file mode 100644 index 000000000..1aaf373ae --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsServerSettingsZoneComponent.json @@ -0,0 +1,29 @@ +{ + "defaultsTitle": "Defaults", + "unitsLabel": "Default units", + "unitsMetric": "Metric", + "unitsImperial": "Imperial", + "currencyLabel": "Default currency", + "numRecordsLabel": "Number of records per page", + "signupTitle": "Sign-up", + "adminApprovalLabel": "Admin approval", + "emailConfirmationLabel": "Email confirmation", + "publicShareableLinksLabel": "Public shareable links", + "enabledLabel": "Enabled", + "serverSettingsPublicShareableLinksEnabledWarningAlert": "Enabling this will make all publicly posted activities viewable without authentication.", + "publicShareableLinksShowUserInfoLabel": "Show user info", + "serverSettingsPublicShareableLinksShowUserWarningAlert": "Enabling this will display user information on all public links", + "photosLabel": "Photos", + "loginPhotoLabel": "Login photo", + "buttonAddPhoto": "Add photo", + "logonPhotoAddLabel": "Login page photo (.png) with a size of 1000x1000 pixels.", + "processingPhotoUpload": "Processing photo upload", + "successPhotoUpload": "Photo uploaded successfully", + "buttonDeleteLoginPhoto": "Delete login photo", + "modalDeleteLoginPhotoBody": "Are you sure you want to delete the login photo?", + "processingPhotoDelete": "Processing photo delete", + "successPhotoDelete": "Photo deleted successfully", + "successUpdateServerSettings": "Server settings updated successfully", + "errorUpdateServerSettings": "Error updating server settings", + "errorFetchingServerSettings": "Error fetching server settings" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsSideBarComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsSideBarComponent.json new file mode 100644 index 000000000..e43abd6c3 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsSideBarComponent.json @@ -0,0 +1,10 @@ +{ + "usersSection": "Users", + "serverSettingsSection": "Server Settings", + "generalSection": "General", + "myProfileSection": "My Profile", + "myGoals": "My Goals", + "securitySection": "Security", + "integrationsSection": "Integrations", + "importSection": "Import" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoals.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoals.json new file mode 100644 index 000000000..2f48bad9d --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoals.json @@ -0,0 +1,7 @@ +{ + "addNewGoal": "Add new goal", + "labelNumberOfGoals1": "You have ", + "labelNumberOfGoals2": " goal(s) set:", + "successGoalDeleted": "Goal deleted successfully", + "errorFetchingGoals": "Error fetching goals" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoalsZone/goalsAddEditGoalModalComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoalsZone/goalsAddEditGoalModalComponent.json new file mode 100644 index 000000000..26c341d8d --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoalsZone/goalsAddEditGoalModalComponent.json @@ -0,0 +1,30 @@ +{ + "addEditGoalModalAddTitle": "Add Goal", + "addEditGoalModalEditTitle": "Edit Goal", + "addEditGoalModalGoalIntervalLabel": "Interval", + "intervalOption1": "Daily", + "intervalOption2": "Weekly", + "intervalOption3": "Monthly", + "intervalOption4": "Yearly", + "addEditGoalModalGoalActivityTypeLabel": "Activity Type", + "activityTypeRun": "Run", + "activityTypeBike": "Bike", + "activityTypeSwim": "Swim", + "activityTypeWalk": "Walk", + "activityTypeStrength": "Strength", + "addEditGoalModalGoalTypeLabel": "Type", + "addEditGoalModalCaloriesLabel": "Calories", + "addEditGoalModalCaloriesPlaceholder": "Target calories", + "addEditGoalModalActivitiesNumberLabel": "Activities number", + "addEditGoalModalActivitiesNumberPlaceholder": "Target activities number", + "addEditGoalModalDistanceLabel": "Distance", + "addEditGoalModalDistancePlaceholder": "Target distance", + "addEditGoalModalElevationLabel": "Elevation", + "addEditGoalModalElevationPlaceholder": "Target elevation", + "addEditGoalModalDurationLabel": "Duration", + "addEditGoalModalDurationPlaceholder": "Target duration (ex: 1.5 equals 1h30m)", + "addEditGoalModalSuccessAddGoal": "Goal added successfully", + "addEditGoalModalErrorAddGoal": "Error adding goal", + "addEditGoalModalSuccessEditGoal": "Goal edited successfully", + "addEditGoalModalErrorEditGoal": "Error editing goal" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoalsZone/goalsListComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoalsZone/goalsListComponent.json new file mode 100644 index 000000000..b8880e31d --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsUserGoalsZone/goalsListComponent.json @@ -0,0 +1,5 @@ +{ + "modalDeleteGoalTitle": "Delete Goal", + "modalDeleteGoalBody": "Are you sure you want to delete the goal with ID ", + "goalDeleteErrorMessage": "Error deleting goal" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsUserProfileZoneComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsUserProfileZoneComponent.json new file mode 100644 index 000000000..60ea305c6 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsUserProfileZoneComponent.json @@ -0,0 +1,89 @@ +{ + "titleProfileInfo": "My profile", + "buttonDeleteProfilePhoto": "Delete", + "modalDeleteProfilePhotoBody": "Are you sure you want to delete your profile photo?", + "buttonEditProfile": "Profile", + "usernameLabel": "Username", + "emailLabel": "Email", + "cityLabel": "City", + "birthdayLabel": "Birthday", + "genderLabel": "Gender", + "genderOption1": "Male", + "genderOption2": "Female", + "genderOption3": "Unspecified", + "unitsLabel": "Units", + "unitsOption1": "Metric", + "unitsOption2": "Imperial", + "currencyLabel": "Currency", + "heightLabel": "Height", + "preferredLanguageLabel": "Preferred language", + "firstDayOfWeekLabel": "First day of week", + "accessTypeLabel": "Access type", + "accessTypeOption1": "Regular user", + "accessTypeOption2": "Administrator", + "userPhotoDeleteSuccess": "Profile photo deleted successfully", + "userPhotoDeleteError": "Error deleting profile photo", + "titleDefaultGear": "Default gear", + "subTitleShoeActivities": "Shoe activities", + "subTitleRun": "Run", + "subTitleTrailRun": "Trail run", + "subTitleVirtualRun": "Virtual run", + "subTitleWalk": "Walk", + "subTitleHike": "Hike", + "subTitleBikeActivities": "Bike activities", + "subTitleBike": "Bike", + "subTitleMTBBike": "MTB bike", + "subTitleGravelBike": "Gravel bike", + "subTitleVirtualBike": "Virtual bike", + "subTitleWaterActivities": "Water activities", + "subTitleSwim": "Open water swim", + "subTitleWindsurf": "Windsurf", + "subTitleRacquetActivities": "Racquet activities", + "subTitleTennis": "Tennis", + "subTitleSnowActivities": "Snow activities", + "subTitleAlpineSki": "Alpine ski", + "subTitleNordicSki": "Nordic ski", + "subTitleSnowboard": "Snowboard", + "selectOptionNotDefined": "Not defined", + "titlePrivacy": "Privacy", + "defaultActivityVisibility": "Default activity visibility", + "privacyOption1": "Public", + "privacyOption2": "Followers", + "privacyOption3": "Private", + "defaultActivityStartTime": "Hide activity start time", + "defaultActivityLocation": "Hide activity location", + "defaultActivityMap": "Hide activity map", + "defaultActivityHeartRate": "Hide activity heart rate", + "defaultActivityPower": "Hide activity power", + "defaultActivityCadence": "Hide activity cadence", + "defaultActivityElevation": "Hide activity elevation", + "defaultActivitySpeed": "Hide activity speed", + "defaultActivityPace": "Hide activity pace", + "defaultActivityLaps": "Hide activity laps", + "defaultActivitySetsSteps": "Hide activity sets/steps", + "defaultActivityGear": "Hide activity gear", + "buttonChangeDefaultActivityVisibility": "Change default visibility", + "buttonChangeUserActivitiesVisibility": "Change activities visibility", + "changeUserActivitiesVisibilityModalVisibilityLabel": "Visibility", + "changeUserActivitiesVisibilityModalButton": "Change", + "successUpdateUserActivitiesVisibility": "Activities visibility updated successfully", + "errorUpdateUserActivitiesVisibility": "Error updating activities visibility", + "errorUnableToGetGear": "Unable to get gear", + "errorUnableToGetDefaultGear": "Unable to get default gear", + "successUpdateDefaultGear": "Default gear updated successfully", + "errorUpdateDefaultGear": "Error updating default gear", + "successUpdateUserPrivacySettings": "User privacy settings updated successfully", + "errorUpdateUserPrivacySettings": "Error updating user privacy settings", + "titleExportData": "Export and import data", + "labelPasswordDisclaimer": "User password is not included in the import/export process. After import, update your password on security section.", + "buttonExportData": "Export data", + "buttonImportData": "Import data", + "modalImportTitle": "Import backup file", + "modalImportBody": "Select the .zip file you previously exported to restore your data.", + "exportLoading": "Exporting data, this may take a few minutes...", + "exportSuccess": "Export completed successfully", + "exportError": "Error exporting data", + "importLoading": "Importing data, this may take a few minutes...", + "importSuccess": "Import completed", + "importError": "Error importing data" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsUserSessionsZone/userSessionsListComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsUserSessionsZone/userSessionsListComponent.json new file mode 100644 index 000000000..3a87aca5f --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsUserSessionsZone/userSessionsListComponent.json @@ -0,0 +1,5 @@ +{ + "badgeCurrentSession": "Current Session", + "modalDeleteSessionTitle": "Delete Session", + "modalDeleteSessionBody": "Are you sure you want to delete session " +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersAddEditUserModalComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersAddEditUserModalComponent.json new file mode 100644 index 000000000..f9ccb345a --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersAddEditUserModalComponent.json @@ -0,0 +1,52 @@ +{ + "addEditUserModalAddTitle": "Add user", + "addEditUserModalEditTitle": "Edit user", + "addEditUserModalEditProfileTitle": "Edit profile", + "addEditUserModalDeleteUserPhotoButton": "Delete photo", + "addEditUserModalUserPhotoLabel": "User photo", + "addEditUserModalUsernameLabel": "Username", + "addEditUserModalUsernamePlaceholder": "Username (max 45 characters)", + "addEditUserModalErrorUsernameExists": "Username already exists", + "addEditUserModalNameLabel": "Name", + "addEditUserModalNamePlaceholder": "Name (max 45 characters)", + "addEditUserModalEmailLabel": "Email", + "addEditUserModalEmailPlaceholder": "Email (max 45 characters)", + "addEditUserModalErrorEmailInvalid": "Email is not valid", + "addEditUserModalErrorEmailExists": "Email already exists", + "addEditUserModalPasswordLabel": "Password", + "addEditUserModalPasswordPlaceholder": "Password", + "addEditUserModalErrorPasswordInvalid": "Password does not meet requirements", + "addEditUserModalCityLabel": "City", + "addEditUserModalCityPlaceholder": "City (max 45 characters)", + "addEditUserModalBirthdayLabel": "Birthday", + "addEditUserModalGenderLabel": "Gender", + "addEditUserModalUnitsLabel": "Units", + "addEditUserModalUnitsOption1": "Metric", + "addEditUserModalUnitsOption2": "Imperial", + "addEditUserModalCurrencyLabel": "Currency", + "addEditUserModalHeightLabel": "Height", + "addEditUserModalHeightPlaceholder": "Height", + "addEditUserModalFeetValidationLabel": "Invalid height. Please enter a valid height in feet.", + "addEditUserModalInchesValidationLabel": "Invalid height. Please enter a valid height in inches.", + "addEditUserModalUserPreferredLanguageLabel": "Preferred language", + "addEditUserModalUserFirstDayOfWeekLabel": "First day of week", + "addEditUserModalUserTypeLabel": "Access type", + "addEditUserModalUserTypeOption1": "Regular user", + "addEditUserModalUserTypeOption2": "Administrator", + "addEditUserModalIsActiveLabel": "Is active", + "addEditUserModalIsActiveOption1": "Yes", + "addEditUserModalIsActiveOption2": "No", + "addEditUserModalDefaultActivityVisibilityLabel": "Default activity visibility", + "addEditUserModalDefaultActivityVisibilityOption1": "Public", + "addEditUserModalDefaultActivityVisibilityOption2": "Followers", + "addEditUserModalDefaultActivityVisibilityOption3": "Private", + "addEditUserModalErrorFetchingUserByUsername": "Error fetching user by username", + "addEditUserModalErrorFetchingUserByEmail": "Error fetching user by email", + "addEditUserModalSuccessDeleteUserPhoto": "User photo deleted successfully", + "addEditUserModalErrorDeleteUserPhoto": "Error deleting user photo", + "addEditUserModalErrorUploadingUserPhoto": "Error uploading user photo", + "addEditUserModalSuccessAddUser": "User added successfully", + "addEditUserModalErrorAddUser": "Error adding user", + "addEditUserModalSuccessEditUser": "User edited successfully", + "addEditUserModalErrorEditUser": "Error editing user" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersChangeUserPasswordModalComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersChangeUserPasswordModalComponent.json new file mode 100644 index 000000000..9ad092ed0 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersChangeUserPasswordModalComponent.json @@ -0,0 +1,10 @@ +{ + "modalChangeUserPasswordTitle": "Change user password", + "modalChangeUserPasswordBodyLabel": "Change password for user ", + "modalChangeUserPasswordPasswordLabel": "New password", + "modalChangeUserPasswordPasswordConfirmationLabel": "Confirm new password", + "modalChangeUserPasswordFeedbackLabel": "Password does not meet requirements", + "modalChangeUserPasswordPasswordsDoNotMatchFeedbackLabel": "Passwords do not match", + "userChangePasswordSuccessMessage": "Password changed successfully", + "userChangePasswordErrorMessage": "Error changing password" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersListComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersListComponent.json new file mode 100644 index 000000000..95080416a --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersListComponent.json @@ -0,0 +1,22 @@ +{ + "userListAccessTypeOption1": "Regular user", + "userListAccessTypeOption2": "Administrator", + "userListUserIsMeBadge": "Me", + "userListUserIsAdminBadge": "Admin", + "userListUserIsInactiveBadge": "Inactive", + "userListUserHasUnverifiedEmailBadge": "Unverified email", + "modalApproveSignUpTitle": "Approve user sign up", + "modalApproveSignUpBody": "Are you sure you want to approve user ", + "processingApproval": "Processing approval...", + "userApproveSuccessMessage": "User approved successfully.", + "userApproveErrorMessage": "Error approving user", + "modalRejectSignUpTitle": "Reject user sign up", + "modalRejectSignUpBody1": "Are you sure you want to reject user ", + "modalRejectSignUpBody2": "The user will be deleted, this cannot be undone.", + "userEditErrorMessage": "Error editing user", + "modalDeleteUserTitle": "Delete user", + "modalDeleteUserBody": "Are you sure you want to delete user ", + "userListUserSessionsTitle": "User sessions", + "userSessionDeleteSuccessMessage": "Session deleted successfully", + "userSessionDeleteErrorMessage": "Error deleting session" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersPasswordRequirementsComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersPasswordRequirementsComponent.json new file mode 100644 index 000000000..f6ebfe887 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZone/usersPasswordRequirementsComponent.json @@ -0,0 +1,7 @@ +{ + "passwordRequirementsTitle": "Password requirements includes:", + "passwordCharacters": "- 8 characters;", + "passwordCapitalLetters": "- 1 capital letter;", + "passwordNumbers": "- 1 number;", + "passwordSpecialCharacters": "- 1 special character;" +} diff --git a/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZoneComponent.json b/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZoneComponent.json new file mode 100644 index 000000000..9b0245cbc --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/settings/settingsUsersZoneComponent.json @@ -0,0 +1,10 @@ +{ + "buttonAddUser": "Add user", + "labelSearchUsersByUsername": "Search users by username", + "labelNumberOfUsers1": "There is a total of ", + "labelNumberOfUsers2": " user(s) (", + "labelNumberOfUsers3": " loaded):", + "successUserAdded": "User added successfully", + "successUserDeleted": "User deleted successfully", + "errorFetchingUsers": "Error fetching users" +} diff --git a/frontend/app/src/i18n/zh-TW/components/users/userDistanceStatsComponent.json b/frontend/app/src/i18n/zh-TW/components/users/userDistanceStatsComponent.json new file mode 100644 index 000000000..be1abc1ff --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/users/userDistanceStatsComponent.json @@ -0,0 +1,4 @@ +{ + "thisWeekDistancesTitle": "Week top 3", + "thisMonthDistancesTitle": "Month top 3" +} diff --git a/frontend/app/src/i18n/zh-TW/components/users/userGoalsStatsComponent.json b/frontend/app/src/i18n/zh-TW/components/users/userGoalsStatsComponent.json new file mode 100644 index 000000000..a7391a0bd --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/components/users/userGoalsStatsComponent.json @@ -0,0 +1,13 @@ +{ + "title": "Goals", + "activityTypeRun": "Run", + "activityTypeBike": "Bike", + "activityTypeSwim": "Swim", + "activityTypeWalk": "Walk", + "activityTypeStrength": "Strength", + "intervalOption1": "Daily", + "intervalOption2": "Weekly", + "intervalOption3": "Monthly", + "intervalOption4": "Yearly", + "activities": "activities" +} diff --git a/frontend/app/src/i18n/zh-TW/emailVerification.json b/frontend/app/src/i18n/zh-TW/emailVerification.json new file mode 100644 index 000000000..8317204c6 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/emailVerification.json @@ -0,0 +1,14 @@ +{ + "verifying": "Verifying your email...", + "pleaseWait": "Please wait while we verify your email address", + "success": "Email Verified!", + "error": "Verification Failed", + "goToLogin": "Go to Login", + "backToLogin": "Back to Login", + "invalidToken": "Invalid or missing verification token", + "verificationSuccess": "Your email has been successfully verified", + "tokenNotFound": "Invalid or expired verification token", + "tokenExpired": "Verification token has expired", + "verificationFailed": "Email verification failed. Please try again", + "emailVerified": "Email verified successfully" +} \ No newline at end of file diff --git a/frontend/app/src/i18n/zh-TW/emailVerificationView.json b/frontend/app/src/i18n/zh-TW/emailVerificationView.json new file mode 100644 index 000000000..7815a375d --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/emailVerificationView.json @@ -0,0 +1,8 @@ +{ + "title1": "Handling verify email", + "title2": "Please wait while your email is being verified. Do not refresh this page.", + "emailVerified": "Email verified successfully!", + "tokenNotFound": "Token not found", + "tokenExpired": "Token expired", + "verificationFailed": "Email verification failed" +} \ No newline at end of file diff --git a/frontend/app/src/i18n/zh-TW/gears/gearView.json b/frontend/app/src/i18n/zh-TW/gears/gearView.json new file mode 100644 index 000000000..fde20a2ea --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/gears/gearView.json @@ -0,0 +1,31 @@ +{ + "buttonAddComponent": "Add component", + "buttonEditGear": "Edit gear", + "buttonDeleteGear": "Delete gear", + "modalDeleteGearBody1": "Are you sure you want to delete gear", + "modalDeleteGearBody2": "This action cannot be undone.", + "gearIsActiveBadge": "Active", + "gearIsInactiveBadge": "Inactive", + "gearTypeOption1": "Bike", + "gearTypeOption2": "Shoes", + "gearTypeOption3": "Wetsuit", + "gearTypeOption4": "Racquet", + "gearTypeOption5": "Skis", + "gearTypeOption6": "Snowboard", + "gearTypeOption7": "Windsurf", + "gearTypeOption8": "Water sports board", + "gearFromStrava": "Strava", + "gearFromGarminConnect": "Garmin Connect", + "labelBrand": "Brand", + "labelModel": "Model", + "labelPurchaseValue": "Purchase value", + "labelTotalCost": "Total cost", + "labelDistance": "Distance", + "labelTime": "Time", + "titleComponents": "Components", + "showInactiveComponents": "Show inactive", + "title": "Activities", + "successGearEdited": "Gear edited successfully", + "errorGearDelete": "Error deleting gear", + "errorFetchingGears": "Error fetching gears" +} diff --git a/frontend/app/src/i18n/zh-TW/gears/gearsView.json b/frontend/app/src/i18n/zh-TW/gears/gearsView.json new file mode 100644 index 000000000..a3933ff4b --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/gears/gearsView.json @@ -0,0 +1,13 @@ +{ + "title": "Gear", + "buttonAddGear": "Add Gear", + "subTitleSearchGearByNickname": "Search gear by nickname", + "placeholderSearchGearByNickname": "Nickname", + "buttonSearchGear": "Search Gear", + "displayUserNumberOfGears1": "There is a total of ", + "displayUserNumberOfGears2": " gear(s) (", + "displayUserNumberOfGears3": " loaded):", + "successGearDeleted": "Gear deleted successfully", + "errorGearNotFound": "Gear not found", + "errorFetchingGears": "Error fetching gears" +} diff --git a/frontend/app/src/i18n/zh-TW/generalItems.json b/frontend/app/src/i18n/zh-TW/generalItems.json new file mode 100644 index 000000000..c3c5ca41a --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/generalItems.json @@ -0,0 +1,75 @@ +{ + "buttonBack": "Back", + "buttonClose": "Close", + "true": "True", + "false": "False", + "yes": "Yes", + "no": "No", + "ofWithSpaces": " of ", + "languageOption1": "English (US)", + "languageOption2": "Catalan (CA)", + "languageOption3": "Portuguese (PT)", + "languageOption4": "German (DE)", + "languageOption5": "French (FR)", + "languageOption6": "Dutch (NL)", + "languageOption7": "Spanish (ES)", + "firstDayOfWeekOption0": "Sunday", + "firstDayOfWeekOption1": "Monday", + "firstDayOfWeekOption2": "Tuesday", + "firstDayOfWeekOption3": "Wednesday", + "firstDayOfWeekOption4": "Thursday", + "firstDayOfWeekOption5": "Friday", + "firstDayOfWeekOption6": "Saturday", + "buttonlistAll": "List all", + "requiredField": "Required fields", + "labelNotApplicable": "N/A", + "labelNoData": "No data", + "unitsCm": "cm", + "unitsCms": "cms", + "unitsM": "m", + "unitsKm": "km", + "unitsKmH": "km/h", + "unitsKg": "kg", + "labelWeightInKg": "Weight in kg", + "unitsInches": "inches", + "unitsFeet": "feet", + "unitsFeetShort": "ft", + "unitsFeetInches": "feet, inches", + "unitsMiles": "mi", + "unitsYards": "yd", + "unitsMph": "mph", + "unitsLbs": "lbs", + "labelWeightInLbs": "Weight in lbs", + "unitsCalories": "kcal", + "unitsBpm": "bpm", + "labelHRinBpm": "Heart rate in bpm", + "unitsWattsShort": "W", + "labelPowerInWatts": "Power in watts", + "labelCadenceInRpm": "Cadence in rpm", + "unitsSpm": "spm", + "labelElevationInMeters": "Elevation in meters", + "labelElevationInFeet": "Elevation in feet", + "labelVelocityInKmH": "Speed in km/h", + "labelVelocityInMph": "Speed in mph", + "labelPaceInMinKm": "Pace in min/km", + "labelPaceInMin100m": "Pace in min/100m", + "labelPaceInMinMile": "Pace in min/mile", + "labelPaceInMin100yd": "Pace in min/100yd", + "labelLaps": "Laps", + "labelRest": "Rest", + "labelStrokeRateInSpm": "Stroke rate in spm", + "startDateLabel": "Start date", + "endDateLabel": "End date", + "cancel": "Cancel", + "loading": "Loading", + "betaTag": " (Beta)", + "currencyEuro": "Euro", + "currencyEuroSymbol": "€", + "currencyDollar": "US Dollar", + "currencyDollarSymbol": "$", + "currencyPound": "British Pound", + "currencyPoundSymbol": "£", + "genderMale": "Male", + "genderFemale": "Female", + "genderUnspecified": "Unspecified" +} diff --git a/frontend/app/src/i18n/zh-TW/healthView.json b/frontend/app/src/i18n/zh-TW/healthView.json new file mode 100644 index 000000000..49e8a26e8 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/healthView.json @@ -0,0 +1,5 @@ +{ + "title": "Health", + "errorFetchingHealthData": "Error fetching health data", + "errorFetchingHealthTargets": "Error fetching health targets" +} diff --git a/frontend/app/src/i18n/zh-TW/homeView.json b/frontend/app/src/i18n/zh-TW/homeView.json new file mode 100644 index 000000000..d8b2d27bb --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/homeView.json @@ -0,0 +1,18 @@ +{ + "title": "Endurain", + "buttonAddActivity": "Add Activity", + "fieldLabelUploadFileType": "Upload .gpx, .fit, .tcx or .gz file", + "radioUserActivities": "My activities", + "radioFollowerActivities": "Followers activities", + "pillIsHidden": "Hidden", + "successActivityAdded": "Activity added successfully", + "errorActivityAdded": "Error adding activity", + "refreshingActivities": "Refreshing activities from linked services", + "successActivitiesRefreshed": "Activities refreshed successfully", + "errorActivityNotFound": "Activity not found", + "processingActivity": "Processing activity", + "successActivityDeleted": "Activity deleted successfully", + "errorFetchingUserStats": "Error fetching user stats", + "errorFetchingUserActivities": "Error fetching user activities", + "errorFetchingMedia": "Error fetching media for activity" +} diff --git a/frontend/app/src/i18n/zh-TW/loginView.json b/frontend/app/src/i18n/zh-TW/loginView.json new file mode 100644 index 000000000..6f39d8899 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/loginView.json @@ -0,0 +1,37 @@ +{ + "sessionExpired": "User session expired", + "logoutSuccess": "You have been successfully logged out", + "error401": "Invalid username or password", + "error403": "You do not have permission to access this resource", + "error500": "It was not possible to connect to the server. Please try again later", + "errorUndefined": "It was not possible to connect to the server. Please try again later", + "subtitle": "Sign-in below", + "username": "Username", + "password": "Password", + "mfaCode": "MFA Code", + "mfaRequired": "Multi-factor authentication required. Please enter your 6-digit code.", + "verifyMFAButton": "Verify MFA", + "invalidMFACode": "Invalid MFA code. Please try again.", + "neverExpires": "Remember me (do not tick this box if you are using a shared computer)", + "signInButton": "Sign in", + "signUpText": "Looking for signing up?", + "signUpButton": "Sign up", + "errorPublicActivityNotFound": "Public activity not found", + "errorPublic_shareable_links": "Public shareable links are not allowed. To view this activity, you must be signed in", + "forgotPassword": "Forgot your password?", + "passwordResetInvalidLink": "No password reset token provided", + "passwordResetSuccess": "Your password has been reset successfully", + "forgotPasswordModalTitle": "Forgot Password", + "forgotPasswordModalEmailLabel": "Email address", + "forgotPasswordModalEmailHelp": "Enter the email address associated with your account. An email with a link to reset your password will be sent.", + "forgotPasswordModalSubmitButton": "Send Reset Link", + "forgotPasswordModalEmailRequired": "Email address is required", + "forgotPasswordModalRequestSuccess": "If the email exists in the system, a password reset link will be sent to the provided email address", + "forgotPasswordModalRequestError": "Failed to process password reset request", + "forgotPasswordModalEmailNotConfigured": "Email service is not configured. Please contact the administrator", + "forgotPasswordModalUnableToSendEmail": "Unable to send email. Please try again later or contact the administrator", + "signUpLink": "Don't have an account? Sign up", + "emailVerificationSent": "Please check your email for verification instructions", + "adminApprovalRequired": "Your account is pending admin approval", + "verifyEmailInvalidLink": "Invalid email verification link" +} diff --git a/frontend/app/src/i18n/zh-TW/notFoundView.json b/frontend/app/src/i18n/zh-TW/notFoundView.json new file mode 100644 index 000000000..e62ef72f5 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/notFoundView.json @@ -0,0 +1,5 @@ +{ + "title": "Oops! Page not found", + "subTitle": "The page you are looking for does not exist or it was changed.", + "backToHomeButton": "Back to home" +} diff --git a/frontend/app/src/i18n/zh-TW/resetPassword.json b/frontend/app/src/i18n/zh-TW/resetPassword.json new file mode 100644 index 000000000..59d812b06 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/resetPassword.json @@ -0,0 +1,11 @@ +{ + "title": "Reset Password", + "newPasswordLabel": "New Password", + "confirmPasswordLabel": "Confirm New Password", + "submitButton": "Reset Password", + "backToLogin": "Back to Login", + "passwordComplexityError": "Password must be at least 8 characters long, include an uppercase letter, a number, and a special character", + "passwordMismatchError": "Passwords do not match", + "resetError": "Failed to reset password", + "invalidOrExpiredToken": "Invalid or expired password reset token" +} diff --git a/frontend/app/src/i18n/zh-TW/searchView.json b/frontend/app/src/i18n/zh-TW/searchView.json new file mode 100644 index 000000000..a3f96789f --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/searchView.json @@ -0,0 +1,37 @@ +{ + "searchSelectLabel": "Search", + "searchSelectOptionActivity": "Activity", + "searchSelectOptionUser": "User", + "searchSelectOptionGear": "Gear", + "searchSelectActivityType0": "All", + "searchSelectActivityType1": "Run", + "searchSelectActivityType2": "Ride", + "searchSelectActivityType3": "Swim", + "searchSelectActivityType4": "Workout", + "searchSelectActivityType5": "Walk", + "searchSelectActivityType6": "Hike", + "searchSelectActivityType7": "Row", + "searchSelectActivityType8": "Yoga", + "searchSelectActivityType9": "Ski", + "searchSelectActivityType10": "Snowboard", + "searchSelectActivityType11": "Racquet sports", + "searchSelectActivityType12": "Windsurf", + "searchSelectActivityType13": "Stand up paddling", + "searchSelectActivityType14": "Surf", + "searchSelectActivityType15": "Ice skate", + "searchSelectActivityType16": "Soccer", + "searchSelectGearType0": "All", + "searchSelectGearType1": "Bike", + "searchSelectGearType2": "Shoes", + "searchSelectGearType3": "Wetsuit", + "searchSelectGearType4": "Racquet", + "searchSelectGearType5": "Skis", + "searchSelectGearType6": "Snowboard", + "searchSelectGearType7": "Windsurf", + "searchSelectGearType8": "Water sports board", + "resultIsInactiveBadge": "Inactive", + "searchInputPlaceholder": "Search text", + "errorFetchingUserWithUsernameContains": "Error fetching user with username contains logic", + "errorFetchingActivityWithNameContains": "Error fetching activity with name contains logic", + "errorFetchingGearWithNicknameContains": "Error fetching gear with nickname contains logic" +} diff --git a/frontend/app/src/i18n/zh-TW/settingsView.json b/frontend/app/src/i18n/zh-TW/settingsView.json new file mode 100644 index 000000000..1ed130b5c --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/settingsView.json @@ -0,0 +1,3 @@ +{ + "title": "Settings" +} diff --git a/frontend/app/src/i18n/zh-TW/signupView.json b/frontend/app/src/i18n/zh-TW/signupView.json new file mode 100644 index 000000000..bbfed0d78 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/signupView.json @@ -0,0 +1,31 @@ +{ + "subtitle": "Create your account below", + "name": "Full Name", + "username": "Username", + "email": "Email Address", + "password": "Password", + "preferredLanguage": "Preferred Language", + "city": "City", + "birthdate": "Birth Date", + "gender": "Gender", + "units": "Units", + "metric": "Metric", + "imperial": "Imperial", + "height": "Height", + "firstDayOfWeek": "First Day of Week", + "currency": "Currency", + "signUpButton": "Create Account", + "alreadyHaveAccount": "Already have an account? Sign in", + "success": "Sign-up successful", + "errorNameRequired": "Full name is required", + "errorUsernameRequired": "Username is required", + "errorEmailRequired": "Email address is required", + "errorEmailInvalid": "Please enter a valid email address", + "errorPasswordRequired": "Password is required", + "errorPasswordTooShort": "Password must be at least 8 characters long", + "errorUserExists": "A user with this email or username already exists", + "errorSignupDisabled": "Sign-up is not enabled on this server", + "errorValidation": "Please check your input and try again", + "errorGeneral": "An error occurred during sign-up", + "signupDisabled": "User sign-up is not enabled on this server" +} \ No newline at end of file diff --git a/frontend/app/src/i18n/zh-TW/strava/stravaCallbackView.json b/frontend/app/src/i18n/zh-TW/strava/stravaCallbackView.json new file mode 100644 index 000000000..aec6e5891 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/strava/stravaCallbackView.json @@ -0,0 +1,4 @@ +{ + "stravaCallbackViewTitle1": "Handling Strava callback", + "stravaCallbackViewTitle2": "Please wait while Strava is being linked to your account. Do not refresh this page." +} diff --git a/frontend/app/src/i18n/zh-TW/summaryView.json b/frontend/app/src/i18n/zh-TW/summaryView.json new file mode 100644 index 000000000..afc91a642 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/summaryView.json @@ -0,0 +1,44 @@ +{ + "title": "Activity summary", + "filterLabelActivityType": "Type", + "filterOptionAllTypes": "All types", + "labelViewType": "View by", + "optionDaily": "Daily", + "optionWeekly": "Weekly", + "optionMonthly": "Monthly", + "optionYearly": "Yearly", + "optionLifetime": "Lifetime", + "labelSelectWeek": "Week", + "labelSelectMonth": "Month", + "labelSelectYear": "Year", + "labelSelectPeriod": "Period", + "buttonPreviousPeriod": "Previous", + "buttonNextPeriod": "Next", + "headerSummaryFor": "Summary for {period}", + "headerBreakdown": "Breakdown", + "headerActivitiesInPeriod": "Activities in period", + "errorLoadingActivityTypes": "Error loading activity types", + "errorLoadingSummary": "Error loading summary", + "errorLoadingSummaryLoad": "Error loading summary on page load", + "errorFetchingActivities": "Error fetching activities", + "noDataForPeriod": "No data for this period.", + "colDay": "Day", + "colWeekNum": "Week #", + "colMonth": "Month", + "colDistance": "Distance", + "colDuration": "Duration", + "colElevation": "Elevation", + "colCalories": "Calories", + "colActivities": "Activities", + "metricTotalDistance": "Total distance", + "metricTotalDuration": "Total duration", + "metricTotalElevation": "Total elevation", + "metricTotalCalories": "Total calories", + "metricTotalActivities": "Total activities", + "invalidYearSelected": "Invalid year selected", + "headerTypeBreakdown": "Breakdown by type", + "colActivityType": "Type", + "headerYear": "Year {year}", + "headerWeekStarting": "Week of {date}", + "colYear": "Year" +} diff --git a/frontend/app/src/i18n/zh-TW/userView.json b/frontend/app/src/i18n/zh-TW/userView.json new file mode 100644 index 000000000..4511e1da2 --- /dev/null +++ b/frontend/app/src/i18n/zh-TW/userView.json @@ -0,0 +1,32 @@ +{ + "thisMonthActivitiesNumber": "This month activities", + "userFollowing": "Following", + "userFollowers": "Followers", + "navigationActivities": "Activities", + "navigationFollowing": "Following", + "navigationFollowers": "Followers", + "navigationUserSettings": "User settings", + "navigationFollow": "Follow", + "modalFollowUserTitle": "Follow user", + "modalFollowUserBody": "Are you sure you want to follow user ", + "errorUnableToSendFollow": "Unable to send follow request to user", + "successFollowRequestSent": "Follow request sent", + "navigationRequestSent": "Request sent", + "modalCancelFollowRequestTitle": "Cancel follow request", + "modalCancelFollowRequestBody": "Are you sure you want to cancel follow request for user ", + "errorUnableToCancelFollowRequest": "Unable to cancel follow request for user", + "successFollowRequestCancelled": "Follow request cancelled", + "navigationUnfollow": "Unfollow", + "modalUnfollowUserTitle": "Unfollow user", + "modalUnfollowUserBody": "Are you sure you want to unfollow user ", + "errorUnableToUnfollow": "Unable to unfollow user", + "successUserUnfollowed": "User unfollowed", + "activitiesPaginationWeek0": "This week", + "activitiesPaginationWeek51": "One year ago", + "successFollowingDeleted": "Following deleted", + "successFollowerDeleted": "Follower deleted", + "successFollowerAccepted": "Follower accepted", + "errorFetchingUserStats": "Error fetching user stats", + "errorFetchingUserFollowers": "Error fetching user followers", + "errorFetchingUserActivities": "Error fetching user activities" +} diff --git a/frontend/app/src/views/SignUpView.vue b/frontend/app/src/views/SignUpView.vue index 04130031a..17f98db99 100644 --- a/frontend/app/src/views/SignUpView.vue +++ b/frontend/app/src/views/SignUpView.vue @@ -9,7 +9,7 @@
    @@ -264,7 +311,8 @@ onMounted(async () => { currency.value = serverSettingsStore.serverSettings.currency numRecordsPerPage.value = serverSettingsStore.serverSettings.num_records_per_page publicShareableLinks.value = serverSettingsStore.serverSettings.public_shareable_links - publicShareableLinksUserInfo.value = serverSettingsStore.serverSettings.public_shareable_links_user_info + publicShareableLinksUserInfo.value = + serverSettingsStore.serverSettings.public_shareable_links_user_info loginPhotoSet.value = serverSettingsStore.serverSettings.login_photo_set signUp.value = serverSettingsStore.serverSettings.signup_enabled adminApproval.value = serverSettingsStore.serverSettings.signup_require_admin_approval @@ -279,7 +327,16 @@ onMounted(async () => { }) watch( - [units, currency, numRecordsPerPage, publicShareableLinks, publicShareableLinksUserInfo, signUp, adminApproval, emailConfirmation], + [ + units, + currency, + numRecordsPerPage, + publicShareableLinks, + publicShareableLinksUserInfo, + signUp, + adminApproval, + emailConfirmation + ], async () => { if (isLoading.value == false) { await updateServerSettings() diff --git a/frontend/app/src/components/Settings/SettingsSideBarComponent.vue b/frontend/app/src/components/Settings/SettingsSideBarComponent.vue index 5bf7fd4fb..a726bffc6 100644 --- a/frontend/app/src/components/Settings/SettingsSideBarComponent.vue +++ b/frontend/app/src/components/Settings/SettingsSideBarComponent.vue @@ -85,14 +85,14 @@ diff --git a/frontend/app/src/components/Settings/SettingsUsersZone/UsersListComponent.vue b/frontend/app/src/components/Settings/SettingsUsersZone/UsersListComponent.vue index ababcc990..d562f87c8 100644 --- a/frontend/app/src/components/Settings/SettingsUsersZone/UsersListComponent.vue +++ b/frontend/app/src/components/Settings/SettingsUsersZone/UsersListComponent.vue @@ -16,68 +16,129 @@
    {{ $t('usersListComponent.userListUserIsMeBadge') }} - {{ $t('usersListComponent.userListUserIsAdminBadge') }} - {{ $t('usersListComponent.userListUserIsInactiveBadge') }} - {{ $t('usersListComponent.userListUserHasUnverifiedEmailBadge') }} + v-if="user.id == authStore.user.id" + >{{ $t('usersListComponent.userListUserIsMeBadge') }} + {{ $t('usersListComponent.userListUserIsAdminBadge') }} + {{ $t('usersListComponent.userListUserIsInactiveBadge') }} + {{ $t('usersListComponent.userListUserHasUnverifiedEmailBadge') }} - - + - + :actionButtonType="`success`" + :actionButtonText="t('usersListComponent.modalApproveSignUpTitle')" + @submitAction="submitApproveSignUp" + v-if="user.pending_admin_approval && user.email_verified" + /> - + - + :actionButtonType="`danger`" + :actionButtonText="t('usersListComponent.modalRejectSignUpTitle')" + @submitAction="submitDeleteUser" + v-if="user.pending_admin_approval && user.email_verified" + /> - + - + - + - +
    @@ -87,10 +148,14 @@
    - +
    - + diff --git a/frontend/app/src/i18n/ca/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/ca/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json index 5a675a94b..cc951107a 100644 --- a/frontend/app/src/i18n/ca/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json +++ b/frontend/app/src/i18n/ca/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -1,4 +1,4 @@ { "title": "New sign-up request", "subTitle": " has requested to sign-up" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/ca/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/ca/components/settings/settingsImportZoneComponent.json index 0fe6a9781..dd8632120 100644 --- a/frontend/app/src/i18n/ca/components/settings/settingsImportZoneComponent.json +++ b/frontend/app/src/i18n/ca/components/settings/settingsImportZoneComponent.json @@ -1,13 +1,13 @@ { - "bulkImportIntegrationTitle": "Bulk import", - "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", - "buttonBulkImport": "Import activities", - "loadingMessageBulkImport": "Importing activities from files...", - "errorMessageUnableToImportActivities": "An error occurred while importing activities", - "stravaGearImportTitle": "Strava gear import", - "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", - "stravaGearImportbuttonBikes": "Import Strava bikes", - "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", - "successMessageStravaBikesImport": "Strava bikes imported successfully", - "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", + "buttonBulkImport": "Import activities", + "loadingMessageBulkImport": "Importing activities from files...", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaGearImportTitle": "Strava gear import", + "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", + "stravaGearImportbuttonBikes": "Import Strava bikes", + "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", + "successMessageStravaBikesImport": "Strava bikes imported successfully", + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" } diff --git a/frontend/app/src/i18n/ca/emailVerificationView.json b/frontend/app/src/i18n/ca/emailVerificationView.json index 7815a375d..c66f2308a 100644 --- a/frontend/app/src/i18n/ca/emailVerificationView.json +++ b/frontend/app/src/i18n/ca/emailVerificationView.json @@ -5,4 +5,4 @@ "tokenNotFound": "Token not found", "tokenExpired": "Token expired", "verificationFailed": "Email verification failed" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/ca/signupView.json b/frontend/app/src/i18n/ca/signupView.json index 27984c5d1..a5bf27da2 100644 --- a/frontend/app/src/i18n/ca/signupView.json +++ b/frontend/app/src/i18n/ca/signupView.json @@ -30,4 +30,4 @@ "errorValidation": "Please check your input and try again", "errorGeneral": "An error occurred during sign-up", "signupDisabled": "User sign-up is not enabled on this server" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/cn/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/cn/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json index 5a675a94b..cc951107a 100644 --- a/frontend/app/src/i18n/cn/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json +++ b/frontend/app/src/i18n/cn/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -1,4 +1,4 @@ { "title": "New sign-up request", "subTitle": " has requested to sign-up" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/cn/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/cn/components/settings/settingsImportZoneComponent.json index 0fe6a9781..dd8632120 100644 --- a/frontend/app/src/i18n/cn/components/settings/settingsImportZoneComponent.json +++ b/frontend/app/src/i18n/cn/components/settings/settingsImportZoneComponent.json @@ -1,13 +1,13 @@ { - "bulkImportIntegrationTitle": "Bulk import", - "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", - "buttonBulkImport": "Import activities", - "loadingMessageBulkImport": "Importing activities from files...", - "errorMessageUnableToImportActivities": "An error occurred while importing activities", - "stravaGearImportTitle": "Strava gear import", - "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", - "stravaGearImportbuttonBikes": "Import Strava bikes", - "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", - "successMessageStravaBikesImport": "Strava bikes imported successfully", - "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", + "buttonBulkImport": "Import activities", + "loadingMessageBulkImport": "Importing activities from files...", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaGearImportTitle": "Strava gear import", + "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", + "stravaGearImportbuttonBikes": "Import Strava bikes", + "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", + "successMessageStravaBikesImport": "Strava bikes imported successfully", + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" } diff --git a/frontend/app/src/i18n/cn/emailVerification.json b/frontend/app/src/i18n/cn/emailVerification.json index 8317204c6..3d943dd36 100644 --- a/frontend/app/src/i18n/cn/emailVerification.json +++ b/frontend/app/src/i18n/cn/emailVerification.json @@ -11,4 +11,4 @@ "tokenExpired": "Verification token has expired", "verificationFailed": "Email verification failed. Please try again", "emailVerified": "Email verified successfully" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/cn/emailVerificationView.json b/frontend/app/src/i18n/cn/emailVerificationView.json index 7815a375d..c66f2308a 100644 --- a/frontend/app/src/i18n/cn/emailVerificationView.json +++ b/frontend/app/src/i18n/cn/emailVerificationView.json @@ -5,4 +5,4 @@ "tokenNotFound": "Token not found", "tokenExpired": "Token expired", "verificationFailed": "Email verification failed" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/cn/signupView.json b/frontend/app/src/i18n/cn/signupView.json index 27984c5d1..a5bf27da2 100644 --- a/frontend/app/src/i18n/cn/signupView.json +++ b/frontend/app/src/i18n/cn/signupView.json @@ -30,4 +30,4 @@ "errorValidation": "Please check your input and try again", "errorGeneral": "An error occurred during sign-up", "signupDisabled": "User sign-up is not enabled on this server" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/de/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/de/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json index 5a675a94b..cc951107a 100644 --- a/frontend/app/src/i18n/de/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json +++ b/frontend/app/src/i18n/de/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -1,4 +1,4 @@ { "title": "New sign-up request", "subTitle": " has requested to sign-up" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/de/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/de/components/settings/settingsImportZoneComponent.json index 0fe6a9781..dd8632120 100644 --- a/frontend/app/src/i18n/de/components/settings/settingsImportZoneComponent.json +++ b/frontend/app/src/i18n/de/components/settings/settingsImportZoneComponent.json @@ -1,13 +1,13 @@ { - "bulkImportIntegrationTitle": "Bulk import", - "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", - "buttonBulkImport": "Import activities", - "loadingMessageBulkImport": "Importing activities from files...", - "errorMessageUnableToImportActivities": "An error occurred while importing activities", - "stravaGearImportTitle": "Strava gear import", - "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", - "stravaGearImportbuttonBikes": "Import Strava bikes", - "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", - "successMessageStravaBikesImport": "Strava bikes imported successfully", - "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", + "buttonBulkImport": "Import activities", + "loadingMessageBulkImport": "Importing activities from files...", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaGearImportTitle": "Strava gear import", + "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", + "stravaGearImportbuttonBikes": "Import Strava bikes", + "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", + "successMessageStravaBikesImport": "Strava bikes imported successfully", + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" } diff --git a/frontend/app/src/i18n/de/emailVerificationView.json b/frontend/app/src/i18n/de/emailVerificationView.json index 7815a375d..c66f2308a 100644 --- a/frontend/app/src/i18n/de/emailVerificationView.json +++ b/frontend/app/src/i18n/de/emailVerificationView.json @@ -5,4 +5,4 @@ "tokenNotFound": "Token not found", "tokenExpired": "Token expired", "verificationFailed": "Email verification failed" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/de/signupView.json b/frontend/app/src/i18n/de/signupView.json index 27984c5d1..a5bf27da2 100644 --- a/frontend/app/src/i18n/de/signupView.json +++ b/frontend/app/src/i18n/de/signupView.json @@ -30,4 +30,4 @@ "errorValidation": "Please check your input and try again", "errorGeneral": "An error occurred during sign-up", "signupDisabled": "User sign-up is not enabled on this server" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/es/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/es/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json index 5a675a94b..cc951107a 100644 --- a/frontend/app/src/i18n/es/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json +++ b/frontend/app/src/i18n/es/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -1,4 +1,4 @@ { "title": "New sign-up request", "subTitle": " has requested to sign-up" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/es/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/es/components/settings/settingsImportZoneComponent.json index 0fe6a9781..dd8632120 100644 --- a/frontend/app/src/i18n/es/components/settings/settingsImportZoneComponent.json +++ b/frontend/app/src/i18n/es/components/settings/settingsImportZoneComponent.json @@ -1,13 +1,13 @@ { - "bulkImportIntegrationTitle": "Bulk import", - "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", - "buttonBulkImport": "Import activities", - "loadingMessageBulkImport": "Importing activities from files...", - "errorMessageUnableToImportActivities": "An error occurred while importing activities", - "stravaGearImportTitle": "Strava gear import", - "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", - "stravaGearImportbuttonBikes": "Import Strava bikes", - "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", - "successMessageStravaBikesImport": "Strava bikes imported successfully", - "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", + "buttonBulkImport": "Import activities", + "loadingMessageBulkImport": "Importing activities from files...", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaGearImportTitle": "Strava gear import", + "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", + "stravaGearImportbuttonBikes": "Import Strava bikes", + "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", + "successMessageStravaBikesImport": "Strava bikes imported successfully", + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" } diff --git a/frontend/app/src/i18n/es/emailVerificationView.json b/frontend/app/src/i18n/es/emailVerificationView.json index 7815a375d..c66f2308a 100644 --- a/frontend/app/src/i18n/es/emailVerificationView.json +++ b/frontend/app/src/i18n/es/emailVerificationView.json @@ -5,4 +5,4 @@ "tokenNotFound": "Token not found", "tokenExpired": "Token expired", "verificationFailed": "Email verification failed" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/es/signupView.json b/frontend/app/src/i18n/es/signupView.json index 27984c5d1..a5bf27da2 100644 --- a/frontend/app/src/i18n/es/signupView.json +++ b/frontend/app/src/i18n/es/signupView.json @@ -30,4 +30,4 @@ "errorValidation": "Please check your input and try again", "errorGeneral": "An error occurred during sign-up", "signupDisabled": "User sign-up is not enabled on this server" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/fr/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/fr/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json index 5a675a94b..cc951107a 100644 --- a/frontend/app/src/i18n/fr/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json +++ b/frontend/app/src/i18n/fr/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -1,4 +1,4 @@ { "title": "New sign-up request", "subTitle": " has requested to sign-up" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/fr/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/fr/components/settings/settingsImportZoneComponent.json index 0fe6a9781..dd8632120 100644 --- a/frontend/app/src/i18n/fr/components/settings/settingsImportZoneComponent.json +++ b/frontend/app/src/i18n/fr/components/settings/settingsImportZoneComponent.json @@ -1,13 +1,13 @@ { - "bulkImportIntegrationTitle": "Bulk import", - "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", - "buttonBulkImport": "Import activities", - "loadingMessageBulkImport": "Importing activities from files...", - "errorMessageUnableToImportActivities": "An error occurred while importing activities", - "stravaGearImportTitle": "Strava gear import", - "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", - "stravaGearImportbuttonBikes": "Import Strava bikes", - "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", - "successMessageStravaBikesImport": "Strava bikes imported successfully", - "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", + "buttonBulkImport": "Import activities", + "loadingMessageBulkImport": "Importing activities from files...", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaGearImportTitle": "Strava gear import", + "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", + "stravaGearImportbuttonBikes": "Import Strava bikes", + "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", + "successMessageStravaBikesImport": "Strava bikes imported successfully", + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" } diff --git a/frontend/app/src/i18n/fr/emailVerificationView.json b/frontend/app/src/i18n/fr/emailVerificationView.json index 7815a375d..c66f2308a 100644 --- a/frontend/app/src/i18n/fr/emailVerificationView.json +++ b/frontend/app/src/i18n/fr/emailVerificationView.json @@ -5,4 +5,4 @@ "tokenNotFound": "Token not found", "tokenExpired": "Token expired", "verificationFailed": "Email verification failed" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/fr/signupView.json b/frontend/app/src/i18n/fr/signupView.json index 27984c5d1..a5bf27da2 100644 --- a/frontend/app/src/i18n/fr/signupView.json +++ b/frontend/app/src/i18n/fr/signupView.json @@ -30,4 +30,4 @@ "errorValidation": "Please check your input and try again", "errorGeneral": "An error occurred during sign-up", "signupDisabled": "User sign-up is not enabled on this server" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/index.js b/frontend/app/src/i18n/index.js index c8d9757d2..565f48ddb 100644 --- a/frontend/app/src/i18n/index.js +++ b/frontend/app/src/i18n/index.js @@ -40,7 +40,8 @@ const componentPaths = { navbarBottomMobileComponent: 'components/navbar/navbarBottomMobileComponent.json', navbarComponent: 'components/navbar/navbarComponent.json', // Notifications components - adminNewSignUpApprovalRequestNotificationComponent: 'components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json', + adminNewSignUpApprovalRequestNotificationComponent: + 'components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json', navbarNotificationsComponent: 'components/notifications/navbarNotificationsComponent.json', newAcceptedRequestNotificationComponent: 'components/notifications/newAcceptedRequestNotificationComponent.json', diff --git a/frontend/app/src/i18n/nl/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/nl/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json index 5a675a94b..cc951107a 100644 --- a/frontend/app/src/i18n/nl/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json +++ b/frontend/app/src/i18n/nl/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -1,4 +1,4 @@ { "title": "New sign-up request", "subTitle": " has requested to sign-up" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/nl/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/nl/components/settings/settingsImportZoneComponent.json index 0fe6a9781..dd8632120 100644 --- a/frontend/app/src/i18n/nl/components/settings/settingsImportZoneComponent.json +++ b/frontend/app/src/i18n/nl/components/settings/settingsImportZoneComponent.json @@ -1,13 +1,13 @@ { - "bulkImportIntegrationTitle": "Bulk import", - "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", - "buttonBulkImport": "Import activities", - "loadingMessageBulkImport": "Importing activities from files...", - "errorMessageUnableToImportActivities": "An error occurred while importing activities", - "stravaGearImportTitle": "Strava gear import", - "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", - "stravaGearImportbuttonBikes": "Import Strava bikes", - "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", - "successMessageStravaBikesImport": "Strava bikes imported successfully", - "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", + "buttonBulkImport": "Import activities", + "loadingMessageBulkImport": "Importing activities from files...", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaGearImportTitle": "Strava gear import", + "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", + "stravaGearImportbuttonBikes": "Import Strava bikes", + "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", + "successMessageStravaBikesImport": "Strava bikes imported successfully", + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" } diff --git a/frontend/app/src/i18n/nl/emailVerificationView.json b/frontend/app/src/i18n/nl/emailVerificationView.json index 7815a375d..c66f2308a 100644 --- a/frontend/app/src/i18n/nl/emailVerificationView.json +++ b/frontend/app/src/i18n/nl/emailVerificationView.json @@ -5,4 +5,4 @@ "tokenNotFound": "Token not found", "tokenExpired": "Token expired", "verificationFailed": "Email verification failed" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/nl/signupView.json b/frontend/app/src/i18n/nl/signupView.json index 27984c5d1..a5bf27da2 100644 --- a/frontend/app/src/i18n/nl/signupView.json +++ b/frontend/app/src/i18n/nl/signupView.json @@ -30,4 +30,4 @@ "errorValidation": "Please check your input and try again", "errorGeneral": "An error occurred during sign-up", "signupDisabled": "User sign-up is not enabled on this server" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/pt/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/pt/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json index 7142431c5..0fc568eae 100644 --- a/frontend/app/src/i18n/pt/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json +++ b/frontend/app/src/i18n/pt/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -1,4 +1,4 @@ { "title": "Novo pedido de inscrição", "subTitle": " pediu para se inscrever" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/pt/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/pt/components/settings/settingsImportZoneComponent.json index 3a533efed..57c0e4ab7 100644 --- a/frontend/app/src/i18n/pt/components/settings/settingsImportZoneComponent.json +++ b/frontend/app/src/i18n/pt/components/settings/settingsImportZoneComponent.json @@ -1,13 +1,13 @@ { - "bulkImportIntegrationTitle": "Importação em massa", - "bulkImportIntegrationBody": "Importação em massa de ficheiros (guardados na diretoria data/activity_files/bulk_import)", - "buttonBulkImport": "Importar atividades", - "loadingMessageBulkImport": "A importar atividades de ficheiros...", - "errorMessageUnableToImportActivities": "Ocorreu um erro ao importar as atividades", - "stravaGearImportTitle": "Importar equipamento do Strava", - "stravaGearImportBody": "Importar equipamento de um Strava takeout (guardados na diretoria data/activity_files/bulk_import)", - "stravaGearImportbuttonBikes": "Importar bicicletas do Strava", - "loadingMessageStravaBikesImport": "A importar bicicletas do Strava...", - "successMessageStravaBikesImport": "Bicicletas importadas do Strava com sucesso", - "errorMessageUnableToImportBikes": "Ocorreu um erro ao importar as bicicletas do Strava" + "bulkImportIntegrationTitle": "Importação em massa", + "bulkImportIntegrationBody": "Importação em massa de ficheiros (guardados na diretoria data/activity_files/bulk_import)", + "buttonBulkImport": "Importar atividades", + "loadingMessageBulkImport": "A importar atividades de ficheiros...", + "errorMessageUnableToImportActivities": "Ocorreu um erro ao importar as atividades", + "stravaGearImportTitle": "Importar equipamento do Strava", + "stravaGearImportBody": "Importar equipamento de um Strava takeout (guardados na diretoria data/activity_files/bulk_import)", + "stravaGearImportbuttonBikes": "Importar bicicletas do Strava", + "loadingMessageStravaBikesImport": "A importar bicicletas do Strava...", + "successMessageStravaBikesImport": "Bicicletas importadas do Strava com sucesso", + "errorMessageUnableToImportBikes": "Ocorreu um erro ao importar as bicicletas do Strava" } diff --git a/frontend/app/src/i18n/pt/emailVerificationView.json b/frontend/app/src/i18n/pt/emailVerificationView.json index 3454bd451..ba852e8a6 100644 --- a/frontend/app/src/i18n/pt/emailVerificationView.json +++ b/frontend/app/src/i18n/pt/emailVerificationView.json @@ -5,4 +5,4 @@ "tokenNotFound": "Token não encontrado", "tokenExpired": "Token expirado", "verificationFailed": "Falha na verificação de e-mail" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/pt/signupView.json b/frontend/app/src/i18n/pt/signupView.json index 05bff87ef..1f5c41f3a 100644 --- a/frontend/app/src/i18n/pt/signupView.json +++ b/frontend/app/src/i18n/pt/signupView.json @@ -30,4 +30,4 @@ "errorValidation": "Por favor, verifique os seus dados e tente novamente", "errorGeneral": "Ocorreu um erro durante o registo", "signupDisabled": "Registo não está habilitado neste servidor" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/tw/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/tw/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json index 5a675a94b..cc951107a 100644 --- a/frontend/app/src/i18n/tw/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json +++ b/frontend/app/src/i18n/tw/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -1,4 +1,4 @@ { "title": "New sign-up request", "subTitle": " has requested to sign-up" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/tw/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/tw/components/settings/settingsImportZoneComponent.json index 0fe6a9781..dd8632120 100644 --- a/frontend/app/src/i18n/tw/components/settings/settingsImportZoneComponent.json +++ b/frontend/app/src/i18n/tw/components/settings/settingsImportZoneComponent.json @@ -1,13 +1,13 @@ { - "bulkImportIntegrationTitle": "Bulk import", - "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", - "buttonBulkImport": "Import activities", - "loadingMessageBulkImport": "Importing activities from files...", - "errorMessageUnableToImportActivities": "An error occurred while importing activities", - "stravaGearImportTitle": "Strava gear import", - "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", - "stravaGearImportbuttonBikes": "Import Strava bikes", - "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", - "successMessageStravaBikesImport": "Strava bikes imported successfully", - "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", + "buttonBulkImport": "Import activities", + "loadingMessageBulkImport": "Importing activities from files...", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaGearImportTitle": "Strava gear import", + "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", + "stravaGearImportbuttonBikes": "Import Strava bikes", + "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", + "successMessageStravaBikesImport": "Strava bikes imported successfully", + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" } diff --git a/frontend/app/src/i18n/tw/emailVerification.json b/frontend/app/src/i18n/tw/emailVerification.json index 8317204c6..3d943dd36 100644 --- a/frontend/app/src/i18n/tw/emailVerification.json +++ b/frontend/app/src/i18n/tw/emailVerification.json @@ -11,4 +11,4 @@ "tokenExpired": "Verification token has expired", "verificationFailed": "Email verification failed. Please try again", "emailVerified": "Email verified successfully" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/tw/emailVerificationView.json b/frontend/app/src/i18n/tw/emailVerificationView.json index 7815a375d..c66f2308a 100644 --- a/frontend/app/src/i18n/tw/emailVerificationView.json +++ b/frontend/app/src/i18n/tw/emailVerificationView.json @@ -5,4 +5,4 @@ "tokenNotFound": "Token not found", "tokenExpired": "Token expired", "verificationFailed": "Email verification failed" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/tw/signupView.json b/frontend/app/src/i18n/tw/signupView.json index 27984c5d1..a5bf27da2 100644 --- a/frontend/app/src/i18n/tw/signupView.json +++ b/frontend/app/src/i18n/tw/signupView.json @@ -30,4 +30,4 @@ "errorValidation": "Please check your input and try again", "errorGeneral": "An error occurred during sign-up", "signupDisabled": "User sign-up is not enabled on this server" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/us/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json b/frontend/app/src/i18n/us/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json index 5a675a94b..cc951107a 100644 --- a/frontend/app/src/i18n/us/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json +++ b/frontend/app/src/i18n/us/components/notifications/adminNewSignUpApprovalRequestNotificationComponent.json @@ -1,4 +1,4 @@ { "title": "New sign-up request", "subTitle": " has requested to sign-up" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json b/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json index 0fe6a9781..dd8632120 100644 --- a/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json +++ b/frontend/app/src/i18n/us/components/settings/settingsImportZoneComponent.json @@ -1,13 +1,13 @@ { - "bulkImportIntegrationTitle": "Bulk import", - "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", - "buttonBulkImport": "Import activities", - "loadingMessageBulkImport": "Importing activities from files...", - "errorMessageUnableToImportActivities": "An error occurred while importing activities", - "stravaGearImportTitle": "Strava gear import", - "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", - "stravaGearImportbuttonBikes": "Import Strava bikes", - "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", - "successMessageStravaBikesImport": "Strava bikes imported successfully", - "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" + "bulkImportIntegrationTitle": "Bulk import", + "bulkImportIntegrationBody": "Bulk import activities from files (in the data/activity_files/bulk_import folder)", + "buttonBulkImport": "Import activities", + "loadingMessageBulkImport": "Importing activities from files...", + "errorMessageUnableToImportActivities": "An error occurred while importing activities", + "stravaGearImportTitle": "Strava gear import", + "stravaGearImportBody": "Import gear from a Strava bulk export (in the data/activity_files/bulk_import folder)", + "stravaGearImportbuttonBikes": "Import Strava bikes", + "loadingMessageStravaBikesImport": "Importing Strava bikes from file...", + "successMessageStravaBikesImport": "Strava bikes imported successfully", + "errorMessageUnableToImportBikes": "An error occurred while importing Strava bikes" } diff --git a/frontend/app/src/i18n/us/emailVerificationView.json b/frontend/app/src/i18n/us/emailVerificationView.json index 7815a375d..c66f2308a 100644 --- a/frontend/app/src/i18n/us/emailVerificationView.json +++ b/frontend/app/src/i18n/us/emailVerificationView.json @@ -5,4 +5,4 @@ "tokenNotFound": "Token not found", "tokenExpired": "Token expired", "verificationFailed": "Email verification failed" -} \ No newline at end of file +} diff --git a/frontend/app/src/i18n/us/signupView.json b/frontend/app/src/i18n/us/signupView.json index 27984c5d1..a5bf27da2 100644 --- a/frontend/app/src/i18n/us/signupView.json +++ b/frontend/app/src/i18n/us/signupView.json @@ -30,4 +30,4 @@ "errorValidation": "Please check your input and try again", "errorGeneral": "An error occurred during sign-up", "signupDisabled": "User sign-up is not enabled on this server" -} \ No newline at end of file +} diff --git a/frontend/app/src/services/signUpService.js b/frontend/app/src/services/signUpService.js index c887ed658..1dc980dd5 100644 --- a/frontend/app/src/services/signUpService.js +++ b/frontend/app/src/services/signUpService.js @@ -1,7 +1,4 @@ -import { - fetchGetRequest, - fetchPostRequest, -} from '@/utils/serviceUtils' +import { fetchGetRequest, fetchPostRequest } from '@/utils/serviceUtils' export const signUp = { signUpRequest(data) { diff --git a/frontend/app/src/services/stravaService.js b/frontend/app/src/services/stravaService.js index 5da902ad8..e9a8932fc 100644 --- a/frontend/app/src/services/stravaService.js +++ b/frontend/app/src/services/stravaService.js @@ -1,4 +1,9 @@ -import { fetchGetRequest, fetchPostRequest, fetchPutRequest, fetchDeleteRequest } from '@/utils/serviceUtils' +import { + fetchGetRequest, + fetchPostRequest, + fetchPutRequest, + fetchDeleteRequest +} from '@/utils/serviceUtils' export const strava = { setUniqueUserStateStravaLink(state) { @@ -22,7 +27,7 @@ export const strava = { window.location.href = stravaAuthUrl }, importBikes() { - return fetchPostRequest('strava/import/bikes'); + return fetchPostRequest('strava/import/bikes') }, linkStravaCallback(state, code, scope) { return fetchPutRequest(`strava/link?state=${state}&code=${code}&scope=${scope}`) diff --git a/frontend/app/src/stores/serverSettingsStore.js b/frontend/app/src/stores/serverSettingsStore.js index 72362f976..b4df7a999 100644 --- a/frontend/app/src/stores/serverSettingsStore.js +++ b/frontend/app/src/stores/serverSettingsStore.js @@ -13,7 +13,7 @@ export const useServerSettingsStore = defineStore('serverSettings', { num_records_per_page: 5, signup_enabled: false, signup_require_admin_approval: null, - signup_require_email_verification: null, + signup_require_email_verification: null } }), actions: { diff --git a/frontend/app/src/types/index.ts b/frontend/app/src/types/index.ts index 666f2c692..940e1c975 100644 --- a/frontend/app/src/types/index.ts +++ b/frontend/app/src/types/index.ts @@ -1,6 +1,6 @@ /** * Common type definitions for the Endurain application - * + * * This file serves as a starting point for TypeScript type definitions. * As you migrate JavaScript code to TypeScript, add relevant types here. */ diff --git a/frontend/app/src/views/ActivityView.vue b/frontend/app/src/views/ActivityView.vue index 2419862ae..59cd4466a 100644 --- a/frontend/app/src/views/ActivityView.vue +++ b/frontend/app/src/views/ActivityView.vue @@ -1,66 +1,96 @@