]> git.laktatnebel.de Git - athletik.triathlon-coaching.com.git/commitdiff
Deepseek-Entwurf implementiert: master
authorOle B. Rosentreter <ole@laktatnebel.de>
Thu, 11 Sep 2025 21:03:18 +0000 (23:03 +0200)
committerOle B. Rosentreter <ole@laktatnebel.de>
Thu, 11 Sep 2025 21:03:18 +0000 (23:03 +0200)
https://chat.deepseek.com/a/chat/s/6dd814bc-36bb-4711-afea-e6df04cdc271

https://ticket.laktatnebel.de/issue.php?issuedetail=170

86 files changed:
athletik-db/pom.xml
athletik-db/sql/data.sql [new file with mode: 0644]
athletik-db/sql/schema.sql
athletik-service/pom.xml [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/AthletikBackendApplication.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/config/SecurityConfig.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/AthletController.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/AuthController.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/ExerciseController.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/GoalController.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/GymController.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/LogController.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/MuscleController.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/ProtokollController.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/ToolController.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/TrainingController.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Athlet.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Exercise.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Goal.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Gym.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Log.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Muscle.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Protokoll.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Tool.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Training.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewAllExercises.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewMusclegroups.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewMuscles.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewProtokollSetsOfToday.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewTools.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewToolweights.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewUsers.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/AthletRepository.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/ExerciseRepository.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/GoalRepository.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/GymRepository.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/LogRepository.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/MuscleRepository.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/ProtokollRepository.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/ToolRepository.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/TrainingRepository.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/AthletService.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/EmailService.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/ExerciseService.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/GoalService.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/GymService.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/LogService.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/MuscleService.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/ProtokollService.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/ToolService.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/TrainingService.java [new file with mode: 0644]
athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/util/AthletikConstants.java [new file with mode: 0644]
athletik-service/src/main/resources/application.yaml [new file with mode: 0644]
athletik-service/src/main/webapp/training-summary.html [new file with mode: 0644]
athletik-web/pom.xml
athletik-web/src/athletik_glob_vars.php [new file with mode: 0644]
athletik-web/src/roadtokona_glob_vars.php [deleted file]
athletik-web/src/webui/img/Big_Ben_London_closeup.jpg [new file with mode: 0644]
athletik-web/src/webui/img/Reichstag_Berlin.jpg [new file with mode: 0644]
athletik-web/src/webui/js/athletik.js [new file with mode: 0644]
athletik-web/src/webui/lib/database_functions.php [new file with mode: 0644]
athletik-web/src/webui/lib/db/database_functions_insert.php [new file with mode: 0644]
athletik-web/src/webui/lib/db/database_functions_pgsql.php [new file with mode: 0644]
athletik-web/src/webui/lib/db/database_functions_select.php [new file with mode: 0644]
athletik-web/src/webui/lib/gui/gui_functions_button.php [new file with mode: 0644]
athletik-web/src/webui/lib/gui/gui_functions_input.php [new file with mode: 0644]
athletik-web/src/webui/lib/gui/gui_functions_select.php [new file with mode: 0644]
athletik-web/src/webui/lib/gui_functions.php [new file with mode: 0644]
athletik-web/src/webui/lib/session/session_functions.php [new file with mode: 0644]
athletik-web/src/webui/lib/session_functions.php [new file with mode: 0644]
athletik-web/src/webui/lib/util/util_functions.php [new file with mode: 0644]
athletik-web/src/webui/lib/util_functions.php [new file with mode: 0644]
athletik-web/src/webui/save-data.php [new file with mode: 0644]
athletik-web/src/webui/stylesheet/athletik.css [new file with mode: 0644]
athletik-web/src/webui/training.php [new file with mode: 0644]
athletik-web/src/webui/training_lasse.html [new file with mode: 0644]
athletik-webapp/AthletikWebApplication.java [new file with mode: 0644]
athletik-webapp/pom.xml [new file with mode: 0644]
athletik-webapp/src/main/webapp/img/Big_Ben_London_closeup.jpg [new file with mode: 0644]
athletik-webapp/src/main/webapp/img/Reichstag_Berlin.jpg [new file with mode: 0644]
athletik-webapp/src/main/webapp/index.html [new file with mode: 0644]
athletik-webapp/src/main/webapp/js/athletik.js [new file with mode: 0644]
athletik-webapp/src/main/webapp/login.html [new file with mode: 0644]
athletik-webapp/src/main/webapp/stylesheet/athletik.css [new file with mode: 0644]
athletik-webapp/src/main/webapp/training.html [new file with mode: 0644]
pom.xml

index 90adcbcd083e0234c912ea505e07c10c39bfa0c5..f041b3ccb71d6d86a87691be09e49e022eef0c94 100644 (file)
@@ -1,18 +1,17 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
-       <groupId>com.triathlon-coaching.product</groupId>
-       <artifactId>athletik.athletik-db</artifactId>
-       <version>0.0.1-SNAPSHOT</version>
+       <artifactId>athletik.db</artifactId>
        <packaging>pom</packaging>
 
-       <name>${product.artifactId}</name>
+       <name>${project.artifactId}</name>
        <description>Athletik Training DB</description>
 
        <parent>
-               <groupId>de.laktatnebel.maven</groupId>
-               <artifactId>laktatnebelscript</artifactId>
-               <version>2.1.9</version>
+               <groupId>com.triathlon-coaching.product</groupId>
+               <artifactId>athletik</artifactId>
+               <version>0.0.1-SNAPSHOT</version>
        </parent>
 
+
 </project>
diff --git a/athletik-db/sql/data.sql b/athletik-db/sql/data.sql
new file mode 100644 (file)
index 0000000..a70f13f
--- /dev/null
@@ -0,0 +1,105 @@
+insert into athletik.users values
+(default, 'oleb', 'Ole', 'training@ole-b-rosentreter.de', 'xyz'),
+(default, 'lasse', 'Lasse', 'email@lasse-gehrmann.de', 'xyz')
+;
+
+insert into athletik.tools values
+(default, 'none', 'kein', 0, 0, 0, ''),
+(default, 'Bodyweight', 'Körpergewicht', 0, 0, 0, ''),
+(default, 'Barbell', 'Langhantel', 20, 2.5, 200, 'Scheiben auf der Langhantel immer mit Klammern sichern !!!'),
+(default, 'Bumbbell', 'Kurzhantel', 10, 2.5, 70, ''),
+(default, 'Mini Dumbbell', 'Minihantel', 1, 1, 20, ''),
+(default, 'Cable', 'Kabelzug', 3.75, 2.5, 100, ''),
+(default, 'Cable', 'Seilzug', 3.75, 2.5, 100, ''),
+(default, 'Cable Row', 'Rudermaschine/Seilzug', 15, 10, 95, ''),
+(default, 'Cable Lat', 'Latzug', 15, 10, 95, ''),
+(default, 'Easy Bar', 'Curlhantel', 20, 2.5, 200, 'Scheiben auf der Curlhantel immer mit Klammern sichern !!!')
+;
+
+insert into athletik.musclegroup values
+(default, 'Upper Arms', 'Oberarme'),
+(default, 'Shoulder', 'Schultern'),
+(default, 'Chest', 'Brust'),
+(default, 'Back', 'Rücken'),
+(default, 'Waist', 'Rumpf'),
+(default, 'Hips', 'Hüfte'),
+(default, 'Tights', 'Ober-/Unterschenkel'),
+(default, 'Calves', 'Wade'),
+(default, 'Neck', 'Hals'),
+(default, 'other', 'Sonstiges')
+;
+
+insert into athletik.muscle values
+(default, 'Triceps Brachii', 'Trizeps', 'Triceps', '', 'Armstreckung', (select id from athletik.musclegroup where name_english='Upper Arms')),
+(default, 'Biceps Brachii', 'Bizeps', 'Biceps', '', 'Armbeugung', (select id from athletik.musclegroup where name_english='Upper Arms')),
+(default, 'Brachialis', '', '', '', 'Armbeugung', (select id from athletik.musclegroup where name_english='Upper Arms')),
+(default, 'Anterior Deltoid', '', '', '', 'Deltamuskel vorne', (select id from athletik.musclegroup where name_english='Shoulder')),
+(default, 'Posterior Deltoid', '', '', '', 'Deltamuskel hinten', (select id from athletik.musclegroup where name_english='Shoulder')),
+(default, 'Lateral Deltoid', '', '', '', 'Deltamuskel seitlich', (select id from athletik.musclegroup where name_english='Shoulder')),
+(default, 'Supraspinatus', '', '', '', '', (select id from athletik.musclegroup where name_english='Shoulder')),
+(default, 'Pectoralis Major Sternal', '', '', '', 'Brustmuskel groß, Ansatz am Brustbein', (select id from athletik.musclegroup where name_english='Chest')),
+(default, 'Pectoralis Major Clavicular', '', '', '', 'Brustmuskel groß, Ansatz am Schlüsselbein', (select id from athletik.musclegroup where name_english='Chest')),
+(default, 'Pectoralis Minor', '', '', '', 'Brustmuskel klein', (select id from athletik.musclegroup where name_english='Chest')),
+(default, 'Serratus Anterior', '', '', '', 'Sägemuskel', (select id from athletik.musclegroup where name_english='Chest'))
+;
+
+
+
+insert into athletik.exercise values
+(default, 'Dumbbell Lying Triceps Extension', '', '', '', 'https://exrx.net/WeightExercises/Triceps/DBLyingTriExt', (select id from athletik.tools where toolname_german='Kurzhantel')),
+(default, 'Cable Pushdown', '', '', '', 'https://exrx.net/WeightExercises/Triceps/CBPushdown', (select id from athletik.tools where toolname_german='Seilzug')),
+(default, 'Barbell Curl', '', '', '', 'https://exrx.net/WeightExercises/Biceps/BBCurl', (select id from athletik.tools where toolname_german='Curlhantel')),
+(default, 'Dumbbell Curl', '', '', '', 'https://exrx.net/WeightExercises/Biceps/DBCurl', (select id from athletik.tools where toolname_german='Kurzhantel')),
+(default, 'Cable Curl', '', '', '', 'https://exrx.net/WeightExercises/Biceps/CBCurl', (select id from athletik.tools where toolname_german='Seilzug')),
+(default, 'Dumbbell Preacher Curl', '', '', '', 'https://exrx.net/WeightExercises/Brachialis/DBPreacherCurl', (select id from athletik.tools where toolname_german='Kurzhantel')),
+(default, 'Dumbbell Shoulder Press', '', '', '', 'https://exrx.net/WeightExercises/DeltoidAnterior/DBShoulderPress', (select id from athletik.tools where toolname_german='Kurzhantel')),
+(default, 'Cable Reverse Fly', '', '', '', 'https://exrx.net/WeightExercises/DeltoidPosterior/CBStandingReverseFly', (select id from athletik.tools where toolname_german='Langhantel')),
+(default, 'Barbell Upright Row', '', '', '', 'https://exrx.net/WeightExercises/DeltoidLateral/BBUprightRow', (select id from athletik.tools where toolname_german='Langhantel')),
+(default, 'Dumbbell Front Lateral Raise', '', '', '', 'https://exrx.net/WeightExercises/Supraspinatus/DBFrontLateralRaise', (select id from athletik.tools where toolname_german='Kurzhantel')),
+(default, 'Dumbbell Bench Press', '', '', '', 'https://exrx.net/WeightExercises/PectoralSternal/DBBenchPress', (select id from athletik.tools where toolname_german='Kurzhantel')),
+(default, 'Barbell Bench Press', '', '', '', 'https://exrx.net/WeightExercises/PectoralSternal/BBBenchPress', (select id from athletik.tools where toolname_german='Langhantel')),
+(default, 'Dumbbell Incline Bench Press', '', '', '', 'https://exrx.net/WeightExercises/PectoralClavicular/DBInclineBenchPress', (select id from athletik.tools where toolname_german='Kurzhantel')),
+(default, 'Barbell Incline Bench Press', '', '', '', 'https://exrx.net/WeightExercises/PectoralClavicular/BBInclineBenchPress', (select id from athletik.tools where toolname_german='Langhantel')),
+(default, 'Cable Incline Fly', '', '', '', 'https://exrx.net/WeightExercises/PectoralClavicular/CBInclineFly', (select id from athletik.tools where toolname_german='Seilzug')),
+(default, 'Cable Standing Fly', '', '', '', 'https://exrx.net/WeightExercises/PectoralSternal/CBStandingFly', (select id from athletik.tools where toolname_german='Seilzug')),
+(default, 'Barbell Incline Shoulder Raise', '', '', '', 'https://exrx.net/WeightExercises/SerratusAnterior/BBInclineShoulderRaise', (select id from athletik.tools where toolname_german='Langhantel')),
+(default, 'Dumbbell Incline Shoulder Raise', '', '', '', 'https://exrx.net/WeightExercises/SerratusAnterior/DBInclineShoulderRaise', (select id from athletik.tools where toolname_german='Kurzhantel'))
+;
+
+
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Dumbbell Lying Triceps Extension'), (select id from athletik.muscle where name_latin='Triceps Brachii'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Cable Pushdown'), (select id from athletik.muscle where name_latin='Triceps Brachii'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Barbell Curl'), (select id from athletik.muscle where name_latin='Biceps Brachii'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Dumbbell Curl'), (select id from athletik.muscle where name_latin='Biceps Brachii'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Cable Curl'), (select id from athletik.muscle where name_latin='Biceps Brachii'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Dumbbell Preacher Curl'), (select id from athletik.muscle where name_latin='Brachialis'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Dumbbell Shoulder Press'), (select id from athletik.muscle where name_latin='Anterior Deltoid'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Cable Reverse Fly'), (select id from athletik.muscle where name_latin='Posterior Deltoid'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Barbell Upright Row'), (select id from athletik.muscle where name_latin='Lateral Deltoid'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Dumbbell Front Lateral Raise'), (select id from athletik.muscle where name_latin='Supraspinatus'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Dumbbell Bench Press'), (select id from athletik.muscle where name_latin='Pectoralis Major Sternal'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Barbell Bench Press'), (select id from athletik.muscle where name_latin='Pectoralis Major Sternal'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Dumbbell Incline Bench Press'), (select id from athletik.muscle where name_latin='Pectoralis Major Clavicular'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Barbell Incline Bench Press'), (select id from athletik.muscle where name_latin='Pectoralis Major Clavicular'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Cable Incline Fly'), (select id from athletik.muscle where name_latin='Pectoralis Major Clavicular'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Cable Standing Fly'), (select id from athletik.muscle where name_latin='Pectoralis Minor'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Barbell Incline Shoulder Raise'), (select id from athletik.muscle where name_latin='Serratus Anterior'));
+insert into athletik.exercise_muscle values
+((select id from athletik.exercise where name_english='Dumbbell Incline Shoulder Raise'), (select id from athletik.muscle where name_latin='Serratus Anterior'));
\ No newline at end of file
index 9cffbf931927360a15a63868f2c5fa551cfa3e3e..d6cc6c09a00036765c008350bd83b20c571f9fd3 100644 (file)
 
+DROP SCHEMA athletik CASCADE;
+
 CREATE SCHEMA athletik;
 
--- Athleten
-CREATE TABLE athletik.users (
-       id SERIAL not null,
-       username varchar not null,
-       name_user varchar not null,
-       email varchar not null,
-       pass varchar not null
+ALTER SCHEMA athletik OWNER TO oleb;
+
+-- athletik.tool definition
+-- Definition des Geräts, ab dem trainiert wird.
+
+CREATE TABLE athletik.tool (
+       id serial4 NOT NULL,
+       toolname_english varchar NOT NULL,
+       toolname_german varchar NOT NULL,
+       min_weight numeric(5, 2) DEFAULT 0 NOT NULL,
+       steps_weight numeric(5, 2) DEFAULT 0 NOT NULL,
+       max_weight numeric(5, 2) DEFAULT 0 NOT NULL,
+       description varchar NULL,
+       CONSTRAINT tool_pkey PRIMARY KEY (id)
 );
 
-ALTER TABLE athletik.users ADD CONSTRAINT users_pkey PRIMARY KEY (id);
-ALTER TABLE athletik.users ADD CONSTRAINT users_ukey UNIQUE (username);
-
--- Geräte
-CREATE TABLE athletik.tools (
-       id SERIAL not null,
-       toolname_english varchar not null,
-       toolname_german varchar not null,
-       min_weight int4 not null default 0,
-       steps_weight int4 not null default 0,
-       description     varchar default null
+-- Permissions
+
+ALTER TABLE athletik.tool OWNER TO oleb;
+GRANT ALL ON TABLE athletik.tool TO oleb;
+
+
+-- athletik.gym definition
+-- Bezeichnung des Fitnessstudios
+
+CREATE TABLE athletik.gym (
+       id serial4 NOT NULL,
+       gymname varchar NOT NULL,
+       gymlocation varchar DEFAULT NULL,
+       CONSTRAINT gym_pkey PRIMARY KEY (id)
 );
 
-ALTER TABLE athletik.tools ADD CONSTRAINT tools_pkey PRIMARY KEY (id);
+-- Permissions
 
--- Muskeln
-CREATE TABLE athletik.muscle (
-       id SERIAL not null,
-       name_latin varchar not null,
-       name_english varchar default null,
-       name_german varchar default null,
-       movement varchar not null
+ALTER TABLE athletik.gym OWNER TO oleb;
+GRANT ALL ON TABLE athletik.gym TO oleb;
+
+
+-- athletik.goal definition
+-- Bezeichnung des Trainingsziels
+
+CREATE TABLE athletik.goal (
+       id serial4 NOT NULL,
+       goalname varchar NOT NULL,
+       description text DEFAULT NULL,
+       CONSTRAINT goal_pkey PRIMARY KEY (id)
+);
+
+-- Permissions
+
+ALTER TABLE athletik.goal OWNER TO oleb;
+GRANT ALL ON TABLE athletik.goal TO oleb;
+
+
+-- athletik.gymtool definition
+-- Kreuztabelle aus Fitnessstudio und Gerät, da jedes Fitnessstudio andere Geräte mit anderen Gewichtsabstufungen haben kann.
+
+CREATE TABLE athletik.gymtool (
+       fk_tool int4 NOT NULL,
+       fk_gym int4 NOT NULL,
+       CONSTRAINT gymtool_ukey UNIQUE (fk_tool, fk_gym),
+       CONSTRAINT gymtool_tool_fkey FOREIGN KEY (fk_tool) REFERENCES athletik.tool(id) ON DELETE CASCADE ON UPDATE CASCADE,
+       CONSTRAINT gymtool_gym_fkey FOREIGN KEY (fk_gym) REFERENCES athletik.gym(id) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+-- Permissions
+
+ALTER TABLE athletik.gymtool OWNER TO oleb;
+GRANT ALL ON TABLE athletik.gymtool TO oleb;
+
+
+
+-- athletik.athlet definition
+-- Der Athlet
+
+CREATE TABLE athletik.athlet (
+       id serial4 NOT NULL,
+       username varchar NOT NULL,
+       fullname varchar NOT NULL,
+       email varchar NOT NULL,
+       pass varchar NOT NULL,
+       CONSTRAINT athlet_pkey PRIMARY KEY (id),
+       CONSTRAINT athlet_ukey UNIQUE (username)
+);     
+
+-- Permissions
+
+ALTER TABLE athletik.athlet OWNER TO oleb;
+GRANT ALL ON TABLE athletik.athlet TO oleb;
+
+
+-- athletik.athletgym definition
+-- Kreuztabelle aus Athlet und Fitnessstudio, da ein Athlet in mehreren Fitnessstudios trainieren könnte
+
+CREATE TABLE athletik.athletgym (
+       fk_athlet int4 NOT NULL,
+       fk_gym int4 NOT NULL,
+       CONSTRAINT athletgym_ukey UNIQUE (fk_athlet, fk_gym),
+       CONSTRAINT athlet_athlet_fkey FOREIGN KEY (fk_athlet) REFERENCES athletik.athlet(id) ON DELETE CASCADE ON UPDATE CASCADE,
+       CONSTRAINT athlet_gym_fkey FOREIGN KEY (fk_gym) REFERENCES athletik.gym(id) ON DELETE CASCADE ON UPDATE CASCADE
 );
 
-ALTER TABLE athletik.muscle ADD CONSTRAINT muscle_pkey PRIMARY KEY (id);
+-- Permissions
+
+ALTER TABLE athletik.athletgym OWNER TO oleb;
+GRANT ALL ON TABLE athletik.athletgym TO oleb;
+
+
+
+-- athletik.exercise definition
+-- Die Übung bestehend aus Namen, Beschreibung einer Video-URL und einem Verweis, an welchem Gerät diese durchgeführt werden soll.
 
--- Übungen
 CREATE TABLE athletik.exercise (
-       id SERIAL not null,
-       name_english varchar not null,
-       description_english varchar default null,
-       name_german varchar default null,
-       description_german varchar default null,
-       url_video varchar default null,
-       
-       fk_tool int4 not null
+       id serial4 NOT NULL,
+       name_english varchar NOT NULL,
+       description_english varchar NULL,
+       name_german varchar NULL,
+       description_german varchar NULL,
+       url_video varchar NULL,
+       fk_tool int4 NOT NULL,
+       CONSTRAINT exercise_pkey PRIMARY KEY (id),
+       CONSTRAINT exercise_tool_fkey FOREIGN KEY (fk_tool) REFERENCES athletik.tool(id) ON DELETE CASCADE ON UPDATE CASCADE
 );
 
-ALTER TABLE athletik.exercise ADD CONSTRAINT exercise_pkey PRIMARY KEY (id);
-ALTER TABLE athletik.exercise ADD CONSTRAINT exercise_tool_fkey FOREIGN KEY (fk_tool) REFERENCES athletik.tools(id) ON UPDATE CASCADE ON DELETE CASCADE;
-       
--- Test
-CREATE TABLE athletik.test (
-       fk_user int4 not null,
-       fk_exercise int4 not null,
-       testdate timestamp not null default now(),
-       fmax decimal default 0
+-- Permissions
+
+ALTER TABLE athletik.exercise OWNER TO oleb;
+GRANT ALL ON TABLE athletik.exercise TO oleb;
+
+
+-- athletik.muscle definition
+-- Name und Beschreibung der Muskeln
+
+CREATE TABLE athletik.muscle (
+       id serial4 NOT NULL,
+       muscle_name_latin varchar NOT NULL,
+       muscle_name_english varchar NULL,
+       muscle_name_german varchar NULL,
+       movement_english varchar NOT NULL,
+       movement_german varchar NOT NULL,
+       musclegroup_name_english varchar NULL,
+       musclegroup_name_german varchar NULL,
+       CONSTRAINT muscle_pkey PRIMARY KEY (id)
 );
 
-ALTER TABLE athletik.test ADD CONSTRAINT test_ukey UNIQUE (fk_exercise, fk_user, testdate);
+-- Permissions
+
+ALTER TABLE athletik.muscle OWNER TO oleb;
+GRANT ALL ON TABLE athletik.muscle TO oleb;
 
-ALTER TABLE athletik.test ADD CONSTRAINT test_exercise_fkey FOREIGN KEY (fk_exercise) REFERENCES athletik.exercise(id) ON UPDATE CASCADE ON DELETE CASCADE;
-ALTER TABLE athletik.test ADD CONSTRAINT test_user_fkey FOREIGN KEY (fk_user) REFERENCES athletik.users(id) ON UPDATE CASCADE ON DELETE CASCADE;
 
--- Protokoll
+-- athletik.protokoll definition
+-- In dieser Tabelle werden die Test- und Trainingsaktivitäten persistiert. Je nach Modus (Test oder Training) werden Spalten leergelassen. 
+
 CREATE TABLE athletik.protokoll (
-       fk_user int4 not null,
-       fk_exercise int4 not null,
-       trainingdate timestamp not null default now(),
-       number_of_set int4 default 1,
-       repetitions int4 default 1,
-       weight int4 default 0
+       fk_athlet int4 NOT NULL,
+       fk_exercise int4 NOT NULL,
+       trainingdate timestamp DEFAULT now() NOT NULL,
+       number_of_set int4 DEFAULT 1,
+       repetitions int4 DEFAULT 1,
+       weight numeric(5, 2) DEFAULT 0,
+       is_test boolean DEFAULT FALSE,
+       CONSTRAINT protokoll_ukey UNIQUE (fk_exercise, fk_athlet, trainingdate, number_of_set),
+       CONSTRAINT protokoll_exercise_fkey FOREIGN KEY (fk_exercise) REFERENCES athletik.exercise(id) ON DELETE CASCADE ON UPDATE CASCADE,
+       CONSTRAINT protokoll_athlet_fkey FOREIGN KEY (fk_athlet) REFERENCES athletik.athlet(id) ON DELETE CASCADE ON UPDATE CASCADE
 );
 
-ALTER TABLE athletik.protokoll ADD CONSTRAINT protokoll_ukey UNIQUE (fk_exercise, fk_user, trainingdate, number_of_set);
+-- Permissions
 
-ALTER TABLE athletik.protokoll ADD CONSTRAINT protokoll_exercise_fkey FOREIGN KEY (fk_exercise) REFERENCES athletik.exercise(id) ON UPDATE CASCADE ON DELETE CASCADE;
-ALTER TABLE athletik.protokoll ADD CONSTRAINT protokoll_user_fkey FOREIGN KEY (fk_user) REFERENCES athletik.users(id) ON UPDATE CASCADE ON DELETE CASCADE;
+ALTER TABLE athletik.protokoll OWNER TO oleb;
+GRANT ALL ON TABLE athletik.protokoll TO oleb;
+
+
+-- athletik.log definition
+-- In der Logtabelle werden die Aktivitäten der Software gespeichert
+
+CREATE TABLE athletik.log (
+       id serial4 NOT NULL,
+       created timestamp DEFAULT now() NOT NULL,
+       logdata text NOT NULL,
+       fk_athlet int4 NOT NULL,
+       sid text NOT NULL,
+       script varchar NOT NULL,
+       CONSTRAINT sqllog_pkey PRIMARY KEY (id),
+       CONSTRAINT sqllog_athlet_fkey FOREIGN KEY (fk_athlet) REFERENCES athletik.athlet(id) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+-- Permissions
+
+ALTER TABLE athletik.log OWNER TO oleb;
+GRANT ALL ON TABLE athletik.log TO oleb;
+
+
+-- athletik.training definition
+-- Diese Tabelle verknüpft den Athleten, die Übungen und das Trainingsziel, die er durchführen kann und stellt damit ein Trainingsprogramm dar.
+-- Dabei werden Gewicht, Sätze und Wiederholungen ergänzt.
+-- Die Zeitstempel versionieren das Ganze.
 
--- Trainingspläne
 CREATE TABLE athletik.training (
-       id SERIAL not null,
-       title varchar not null,
-       description text default null,
-       fk_user int4 not null,
-       fk_exercise int4 not null,
-       repetitions_of_set int4 default 1,
-       repetitions int4 default 1,
-       factor_of_fmax int4 default 0,
-       created timestamp not null default now(),
-       valid_since timestamp not null default now()
+       id serial4 NOT NULL,
+       title varchar NOT NULL,
+       fk_goal int4 NOT NULL, -- Zweck des Trainings. Kraftausdauer, Schnellkraft, ...
+       description text NULL,
+       fk_athlet int4 NOT NULL,
+       fk_exercise int4 NOT NULL,
+       repetitions_of_set int4 DEFAULT 1 NULL,
+       repetitions int4 DEFAULT 1 NULL,
+       factor_of_fmax numeric(5, 2) DEFAULT 0 NULL,
+       created timestamp DEFAULT now() NOT NULL,
+       valid_since timestamp DEFAULT now() NOT NULL,
+       CONSTRAINT training_ukey UNIQUE (fk_exercise, fk_athlet, repetitions_of_set, repetitions, factor_of_fmax),
+       CONSTRAINT training_goal_fkey FOREIGN KEY (fk_goal) REFERENCES athletik.goal(id) ON DELETE CASCADE ON UPDATE CASCADE,
+       CONSTRAINT training_exercise_fkey FOREIGN KEY (fk_exercise) REFERENCES athletik.exercise(id) ON DELETE CASCADE ON UPDATE CASCADE,
+       CONSTRAINT training_athlet_fkey FOREIGN KEY (fk_athlet) REFERENCES athletik.athlet(id) ON DELETE CASCADE ON UPDATE CASCADE
 );
 
-ALTER TABLE athletik.training ADD CONSTRAINT training_ukey UNIQUE (fk_exercise, fk_user, repetitions_of_set, repetitions, factor_of_fmax);
+-- Permissions
+
+ALTER TABLE athletik.training OWNER TO oleb;
+GRANT ALL ON TABLE athletik.training TO oleb;
+
 
-ALTER TABLE athletik.training ADD CONSTRAINT training_exercise_fkey FOREIGN KEY (fk_exercise) REFERENCES athletik.exercise(id) ON UPDATE CASCADE ON DELETE CASCADE;
-ALTER TABLE athletik.training ADD CONSTRAINT training_user_fkey FOREIGN KEY (fk_user) REFERENCES athletik.users(id) ON UPDATE CASCADE ON DELETE CASCADE;
+-- athletik.exercise_muscle definition
+-- Eine Kreuztabelle, die Übungen und Muskeln zusammenführt.
+-- Eine Übung ist meistens für mehrere Muskeln geeinet, aber für wenige primär.
 
--- Log
-CREATE TABLE athletik.sqllog (
-       id SERIAL not null,
-       created timestamp not null default now(),
-       sql text not null,
-       fk_user int4 not null,
-       sid text not null,
-       script varchar not null
+CREATE TABLE athletik.exercise_muscle (
+       fk_exercise int4 NOT NULL,
+       fk_muscle int4 NOT NULL,
+       isprimary boolean DEFAULT false,
+       CONSTRAINT exercise_muscle_ukey UNIQUE (fk_exercise, fk_muscle),
+       CONSTRAINT exercise_muscle_exercise_fkey FOREIGN KEY (fk_exercise) REFERENCES athletik.exercise(id) ON DELETE CASCADE ON UPDATE CASCADE,
+       CONSTRAINT exercise_muscle_muscle_fkey FOREIGN KEY (fk_muscle) REFERENCES athletik.muscle(id) ON DELETE CASCADE ON UPDATE CASCADE
 );
 
-ALTER TABLE athletik.sqllog ADD CONSTRAINT sqllog_pkey PRIMARY KEY (id);
-   
-ALTER TABLE athletik.sqllog ADD CONSTRAINT sqllog_user_fkey FOREIGN KEY (fk_user) REFERENCES athletik.users(id) ON UPDATE CASCADE ON DELETE CASCADE;
+-- Permissions
 
+ALTER TABLE athletik.exercise_muscle OWNER TO oleb;
+GRANT ALL ON TABLE athletik.exercise_muscle TO oleb;
 
 
+-- athletik.view_allexercises source
 
--- ALTER SCHEMA athletik OWNER TO laktatnebel;
+CREATE OR REPLACE VIEW athletik.view_allexercises
+AS SELECT e.id AS id_exercise,
+    e.name_english,
+    e.description_english,
+    e.name_german,
+    e.description_german,
+    e.url_video,
+    t.id AS id_tool,
+    t.toolname_english,
+    t.toolname_german,
+    t.min_weight,
+    t.max_weight,
+    t.steps_weight,
+    t.description,
+    em.isprimary,
+    m.id,
+    m.muscle_name_latin,
+    m.muscle_name_english,
+    m.muscle_name_german,
+    m.movement_english,
+    m.movement_german,
+    m.musclegroup_name_english,
+    m.musclegroup_name_german
+   FROM athletik.exercise_muscle em
+     JOIN athletik.muscle m ON m.id = em.fk_muscle
+     JOIN athletik.exercise e ON e.id = em.fk_exercise
+     JOIN athletik.tool t ON t.id = e.fk_tool;
 
--- ALTER TABLE athletik.users OWNER TO laktatnebel;
--- ALTER TABLE athletik.tools OWNER TO laktatnebel;
--- ALTER TABLE athletik.muscle OWNER TO laktatnebel;
--- ALTER TABLE athletik.exercise OWNER TO laktatnebel;
--- ALTER TABLE athletik.test OWNER TO laktatnebel;
--- ALTER TABLE athletik.protokoll OWNER TO laktatnebel;
--- ALTER TABLE athletik.training OWNER TO laktatnebel;
--- ALTER TABLE athletik.sqllog OWNER TO laktatnebel;
+-- Permissions
 
--- GRANT ALL ON SCHEMA athletik TO laktatnebel;
+ALTER TABLE athletik.view_allexercises OWNER TO oleb;
+GRANT ALL ON TABLE athletik.view_allexercises TO oleb;
 
 
-ALTER SCHEMA athletik OWNER TO oleb;
+-- athletik.view_musclegroups source
 
-ALTER TABLE athletik.users OWNER TO oleb;
-ALTER TABLE athletik.tools OWNER TO oleb;
-ALTER TABLE athletik.muscle OWNER TO oleb;
-ALTER TABLE athletik.exercise OWNER TO oleb;
-ALTER TABLE athletik.test OWNER TO oleb;
-ALTER TABLE athletik.protokoll OWNER TO oleb;
-ALTER TABLE athletik.training OWNER TO oleb;
-ALTER TABLE athletik.sqllog OWNER TO oleb;
+CREATE OR REPLACE VIEW athletik.view_musclegroups
+AS SELECT DISTINCT 
+    m.musclegroup_name_english,
+    m.musclegroup_name_german
+   FROM athletik.muscle m;
+
+-- Permissions
+
+ALTER TABLE athletik.view_musclegroups OWNER TO oleb;
+GRANT ALL ON TABLE athletik.view_musclegroups TO oleb;
+
+
+-- athletik.view_muscles source
+
+CREATE OR REPLACE VIEW athletik.view_muscles
+AS SELECT m.id,
+    m.muscle_name_latin,
+    m.muscle_name_english,
+    m.muscle_name_german,
+    m.movement_english,
+    m.movement_german,
+    m.musclegroup_name_english,
+    m.musclegroup_name_german
+   FROM athletik.muscle m;
+
+-- Permissions
+
+ALTER TABLE athletik.view_muscles OWNER TO oleb;
+GRANT ALL ON TABLE athletik.view_muscles TO oleb;
+
+
+-- athletik.view_protokollsetsoftoday source
+
+CREATE OR REPLACE VIEW athletik.view_protokollsetsoftoday
+AS SELECT fk_athlet,
+    fk_exercise,
+    trainingdate,
+    number_of_set
+   FROM athletik.protokoll p;
+
+-- Permissions
+
+ALTER TABLE athletik.view_protokollsetsoftoday OWNER TO oleb;
+GRANT ALL ON TABLE athletik.view_protokollsetsoftoday TO oleb;
+
+
+-- athletik.view_tool source
+
+CREATE OR REPLACE VIEW athletik.view_tool
+AS SELECT id,
+    toolname_english,
+    toolname_german,
+    min_weight,
+    steps_weight,
+    max_weight,
+    description
+   FROM athletik.tool t;
+
+-- Permissions
+
+ALTER TABLE athletik.view_tool OWNER TO oleb;
+GRANT ALL ON TABLE athletik.view_tool TO oleb;
+
+
+-- athletik.view_toolweights source
+
+CREATE OR REPLACE VIEW athletik.view_toolweights
+AS SELECT id,
+    min_weight,
+    steps_weight,
+    max_weight
+   FROM athletik.tool t;
+
+-- Permissions
+
+ALTER TABLE athletik.view_toolweights OWNER TO oleb;
+GRANT ALL ON TABLE athletik.view_toolweights TO oleb;
+
+
+-- athletik.view_athlet source
+
+CREATE OR REPLACE VIEW athletik.view_athlet
+AS SELECT id,
+    username,
+    fullname,
+    email,
+    pass
+   FROM athletik.athlet u;
+
+-- Permissions
+
+ALTER TABLE athletik.view_athlet OWNER TO oleb;
+GRANT ALL ON TABLE athletik.view_athlet TO oleb;
 
 GRANT ALL ON SCHEMA athletik TO oleb;
 
diff --git a/athletik-service/pom.xml b/athletik-service/pom.xml
new file mode 100644 (file)
index 0000000..60cdd77
--- /dev/null
@@ -0,0 +1,365 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <artifactId>athletik.service</artifactId>
+
+       <name>${project.artifactId}</name>
+       <description>Athletik Training Sevice</description>
+
+       <parent>
+               <groupId>com.triathlon-coaching.product</groupId>
+               <artifactId>athletik</artifactId>
+               <version>0.0.1-SNAPSHOT</version>
+       </parent>
+
+       <dependencyManagement>
+               <dependencies>
+                       <!-- Spring Boot -->
+                       <dependency>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-dependencies</artifactId>
+                               <version>${spring-boot.version}</version>
+                               <type>pom</type>
+                               <scope>import</scope>
+                       </dependency>
+
+                       <!-- Common Dependencies -->
+                       <dependency>
+                               <groupId>org.postgresql</groupId>
+                               <artifactId>postgresql</artifactId>
+                               <version>${postgresql.version}</version>
+                       </dependency>
+
+                       <!-- JEE -->
+                       <dependency>
+                               <groupId>javax.servlet</groupId>
+                               <artifactId>javax.servlet-api</artifactId>
+                               <version>${javax.servlet-api.version}</version>
+                               <scope>provided</scope>
+                       </dependency>
+                       <dependency>
+                               <groupId>org.hibernate</groupId>
+                               <artifactId>hibernate-core</artifactId>
+                               <version>${hibernate-core.version}</version>
+                       </dependency>
+
+                       <!-- Mail -->
+                       <dependency>
+                               <groupId>javax.mail</groupId>
+                               <artifactId>javax.mail-api</artifactId>
+                               <version>${javax.mail.version}</version>
+                       </dependency>
+                       <dependency>
+                               <groupId>com.sun.mail</groupId>
+                               <artifactId>javax.mail</artifactId>
+                               <version>${javax.mail.version}</version>
+                       </dependency>
+
+                       <!-- Spring -->
+                       <dependency>
+                               <groupId>org.springframework</groupId>
+                               <artifactId>spring-webmvc</artifactId>
+                               <version>${springframework.version}</version>
+                       </dependency>
+                       <dependency>
+                               <groupId>org.springframework</groupId>
+                               <artifactId>spring-orm</artifactId>
+                               <version>${springframework.version}</version>
+                       </dependency>
+
+                       <!-- JPA -->
+                       <dependency>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-starter-data-jpa</artifactId>
+                               <version>${spring-boot.version}</version>
+                       </dependency>
+
+                       <!-- Lombok -->
+                       <dependency>
+                               <groupId>org.projectlombok</groupId>
+                               <artifactId>lombok</artifactId>
+                               <version>${lombok.version}</version>
+                               <scope>provided</scope>
+                       </dependency>
+
+                       <!-- Validation -->
+                       <dependency>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-starter-validation</artifactId>
+                               <version>${spring-boot.version}</version>
+                       </dependency>
+
+                       <!-- Security -->
+                       <dependency>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-starter-security</artifactId>
+                               <version>${spring-boot.version}</version>
+                       </dependency>
+
+                       <!-- Email -->
+                       <dependency>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-starter-mail</artifactId>
+                               <version>${spring-boot.version}</version>
+                       </dependency>
+
+                       <!-- Thymeleaf -->
+                       <!-- WebUI-->
+                       <dependency>
+                               <groupId>org.thymeleaf</groupId>
+                               <artifactId>thymeleaf-spring5</artifactId>
+                               <version>${thymeleaf.version}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>de.laktatnebel.libs</groupId>
+                               <artifactId>utillib</artifactId>
+                               <version>${utillib.version}</version>
+                       </dependency>
+               </dependencies>
+       </dependencyManagement>
+
+
+       <dependencies>
+
+               <!-- Common Dependencies -->
+               <dependency>
+                       <groupId>org.postgresql</groupId>
+                       <artifactId>postgresql</artifactId>
+               </dependency>
+
+               <!-- JEE -->
+               <dependency>
+                       <groupId>org.hibernate</groupId>
+                       <artifactId>hibernate-core</artifactId>
+               </dependency>
+
+               <!-- Servlet API (provided by Tomcat) -->
+               <dependency>
+                       <groupId>javax.servlet</groupId>
+                       <artifactId>javax.servlet-api</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Mail -->
+               <dependency>
+                       <groupId>javax.mail</groupId>
+                       <artifactId>javax.mail-api</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>com.sun.mail</groupId>
+                       <artifactId>javax.mail</artifactId>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.springframework</groupId>
+                       <artifactId>spring-webmvc</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework</groupId>
+                       <artifactId>spring-orm</artifactId>
+               </dependency>
+
+               <!-- JPA -->
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-data-jpa</artifactId>
+               </dependency>
+
+               <!-- Lombok -->
+               <dependency>
+                       <groupId>org.projectlombok</groupId>
+                       <artifactId>lombok</artifactId>
+               </dependency>
+
+               <!-- Validation -->
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-validation</artifactId>
+               </dependency>
+
+               <!-- Security -->
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-security</artifactId>
+               </dependency>
+
+               <!-- Email -->
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-mail</artifactId>
+               </dependency>
+
+               <!-- Test -->
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-test</artifactId>
+                       <scope>test</scope>
+               </dependency>
+
+               <!-- Thymeleaf -->
+               <!-- WebUI-->
+               <dependency>
+                       <groupId>org.thymeleaf</groupId>
+                       <artifactId>thymeleaf-spring5</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>de.laktatnebel.libs</groupId>
+                       <artifactId>utillib</artifactId>
+               </dependency>
+       </dependencies>
+
+
+       <build>
+               <finalName>${project.artifactId}</finalName>
+               <pluginManagement>
+                       <plugins>
+                               <plugin>
+                                       <groupId>org.springframework.boot</groupId>
+                                       <artifactId>spring-boot-maven-plugin</artifactId>
+                                       <version>${spring-boot.version}</version>
+                               </plugin>
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-compiler-plugin</artifactId>
+                                       <version>${maven-compiler-plugin.version}</version>
+                                       <configuration>
+                                               <source>${java.version}</source>
+                                               <target>${java.version}</target>
+                                       </configuration>
+                               </plugin>
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-javadoc-plugin</artifactId>
+                                       <version>${maven-javadoc-plugin.version}</version>
+                               </plugin>
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-enforcer-plugin</artifactId>
+                                       <version>${maven-enforcer-plugin.version}</version>
+                               </plugin>
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-source-plugin</artifactId>
+                                       <version>${maven-source-plugin.version}</version>
+                               </plugin>
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-deploy-plugin</artifactId>
+                                       <version>${maven-deploy-plugin.version}</version>
+                               </plugin>
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-release-plugin</artifactId>
+                                       <version>${maven-release-plugin.version}</version>
+                                       <configuration>
+                                               <branchName>develop</branchName>
+                                               <pushChanges>false</pushChanges>
+                                               <localCheckout>true</localCheckout>
+                                       </configuration>
+                               </plugin>
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-site-plugin</artifactId>
+                                       <version>${maven-site-plugin.version}</version>
+                                       <executions>
+                                               <execution>
+                                                       <goals>
+                                                               <goal>site</goal>
+                                                       </goals>
+                                                       <configuration>
+                                                               <!--
+                                                               <reportPlugins>
+                                                                       <plugin>
+                                                                               <artifactId>maven-javadoc-plugin</artifactId>
+                                                                       </plugin>
+                                                               </reportPlugins>
+                                                               -->
+                                                       </configuration>
+                                               </execution>
+                                       </executions>
+                               </plugin>
+                       </plugins>
+               </pluginManagement>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-javadoc-plugin</artifactId>
+                               <configuration>
+                                       <additionalparam>${javadoc.opts}</additionalparam>
+                                       <doclint>none</doclint>
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <id>attach-javadocs</id>
+                                               <goals>
+                                                       <goal>jar</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <additionalparam>${javadoc.opts}</additionalparam>
+                                                       <doclint>none</doclint>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-site-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                               <configuration>
+                                       <release>${java.version}</release>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <artifactId>maven-enforcer-plugin</artifactId>
+                               <executions>
+                                       <execution>
+                                               <id>enforce-versions</id>
+                                               <goals>
+                                                       <goal>enforce</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                               <configuration>
+                                       <!--
+                                       http://maven.apache.org/enforcer/enforcer-rules/versionRanges.html
+                                       -->
+                                       <rules>
+                                               <requireMavenVersion>
+                                                       <version>[3.0,)</version>
+                                               </requireMavenVersion>
+                                               <requireJavaVersion>
+                                                       <version>${java.version}</version>
+                                               </requireJavaVersion>
+                                       </rules>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.tomcat.maven</groupId>
+                               <artifactId>tomcat7-maven-plugin</artifactId>
+                               <version>2.0</version>
+                               <configuration>
+                                       <server>localtomcat</server>
+                                       <url>http://localhost:8080/manager/text</url>
+                                       <!-- path>/${project.build.finalName}</path -->
+                                       <update>true</update>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-maven-plugin</artifactId>
+                               <configuration>
+                                       <mainClass>
+                                               ${project.groupId}.${project.artifactId}.AthletikBackendApplication</mainClass>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+
+</project>
+
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/AthletikBackendApplication.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/AthletikBackendApplication.java
new file mode 100644 (file)
index 0000000..fe65611
--- /dev/null
@@ -0,0 +1,17 @@
+package com.triathlon_coaching.product.athletik.backend;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+@SpringBootApplication
+@EntityScan("com.triathlon_coaching.product.athletik.models")
+@EnableJpaRepositories("com.triathlon_coaching.product.athletik.backend.repositories")
+@ComponentScan({"com.triathlon_coaching.product.athletik.backend", "com.triathlon_coaching.product.athletik.services"})
+public class AthletikBackendApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(AthletikBackendApplication.class, args);
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/config/SecurityConfig.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/config/SecurityConfig.java
new file mode 100644 (file)
index 0000000..1da2d94
--- /dev/null
@@ -0,0 +1,74 @@
+package com.triathlon_coaching.product.athletik.backend.config;
+
+import com.triathlon_coaching.product.athletik.backend.service.AthletService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+
+import javax.sql.DataSource;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+       @Autowired
+       private DataSource dataSource;
+
+       @Autowired
+       private AthletService athletService;
+
+       @Bean
+       public PasswordEncoder passwordEncoder() {
+               return new PasswordEncoder() {
+                       @Override
+                       public String encode(CharSequence rawPassword) {
+                               try {
+                                       MessageDigest digest = MessageDigest.getInstance("SHA-512");
+                                       byte[] hash = digest.digest(rawPassword.toString().getBytes());
+                                       StringBuilder hexString = new StringBuilder();
+                                       for (byte b : hash) {
+                                               String hex = Integer.toHexString(0xff & b);
+                                               if (hex.length() == 1)
+                                                       hexString.append('0');
+                                               hexString.append(hex);
+                                       }
+                                       return hexString.toString();
+                               } catch (NoSuchAlgorithmException e) {
+                                       throw new RuntimeException("SHA-512 Algorithmus nicht verfügbar", e);
+                               }
+                       }
+
+                       @Override
+                       public boolean matches(CharSequence rawPassword, String encodedPassword) {
+                               return encode(rawPassword).equals(encodedPassword);
+                       }
+               };
+       }
+
+       @Override
+       protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+               auth.jdbcAuthentication().dataSource(dataSource)
+                               .usersByUsernameQuery("SELECT username, pass, true FROM athletik.athlet WHERE username = ?")
+                               .authoritiesByUsernameQuery("SELECT username, 'USER' FROM athletik.athlet WHERE username = ?")
+                               .passwordEncoder(passwordEncoder());
+       }
+
+       @Override
+       protected void configure(HttpSecurity http) throws Exception {
+               http.authorizeRequests().antMatchers("/", "/home", "/api/auth/register", "/css/**", "/js/**", "/img/**")
+                               .permitAll().anyRequest().authenticated().and().formLogin().loginPage("/login")
+                               .defaultSuccessUrl("/training", true).permitAll().and().logout()
+                               .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login?logout")
+                               .permitAll().and().csrf().ignoringAntMatchers("/api/**") // CSRF für API-Endpunkte deaktivieren
+                               .and().sessionManagement().sessionFixation().migrateSession();
+       }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/AthletController.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/AthletController.java
new file mode 100644 (file)
index 0000000..982e907
--- /dev/null
@@ -0,0 +1,144 @@
+package com.triathlon_coaching.product.athletik.backend.contoller;
+
+import com.triathlon_coaching.product.athletik.backend.model.Athlet;
+import com.triathlon_coaching.product.athletik.backend.service.AthletService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpSession;
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Optional;
+
+@RestController
+@RequestMapping("/api/athleten")
+public class AthletController {
+    
+    @Autowired
+    private AthletService athletService;
+    
+    @GetMapping
+    public ResponseEntity<List<Athlet>> getAllAthleten() {
+        List<Athlet> athleten = athletService.findAllAthleten();
+        return ResponseEntity.ok(athleten);
+    }
+    
+    @GetMapping("/{id}")
+    public ResponseEntity<Athlet> getAthletById(@PathVariable Long id) {
+        Optional<Athlet> athlet = athletService.findAthletById(id);
+        return athlet.map(ResponseEntity::ok)
+                   .orElse(ResponseEntity.notFound().build());
+    }
+    
+    @GetMapping("/username/{username}")
+    public ResponseEntity<Athlet> getAthletByUsername(@PathVariable String username) {
+        Optional<Athlet> athlet = athletService.findAthletByUsername(username);
+        return athlet.map(ResponseEntity::ok)
+                   .orElse(ResponseEntity.notFound().build());
+    }
+    
+    @GetMapping("/email/{email}")
+    public ResponseEntity<Athlet> getAthletByEmail(@PathVariable String email) {
+        Optional<Athlet> athlet = athletService.findAthletByEmail(email);
+        return athlet.map(ResponseEntity::ok)
+                   .orElse(ResponseEntity.notFound().build());
+    }
+    
+    @GetMapping("/gym/{gymId}")
+    public ResponseEntity<List<Athlet>> getAthletenByGymId(@PathVariable Long gymId) {
+        List<Athlet> athleten = athletService.findAthletenByGymId(gymId);
+        return ResponseEntity.ok(athleten);
+    }
+    
+    @PostMapping
+    public ResponseEntity<?> createAthlet(@Valid @RequestBody Athlet athlet) {
+        if (athletService.existsByUsername(athlet.getUsername())) {
+            return ResponseEntity.badRequest().body("Username already exists");
+        }
+        if (athletService.existsByEmail(athlet.getEmail())) {
+            return ResponseEntity.badRequest().body("Email already exists");
+        }
+        
+        Athlet savedAthlet = athletService.saveAthlet(athlet);
+        return ResponseEntity.ok(savedAthlet);
+    }
+    
+    @PutMapping("/{id}")
+    public ResponseEntity<Athlet> updateAthlet(@PathVariable Long id, @Valid @RequestBody Athlet athletDetails) {
+        Optional<Athlet> athletOptional = athletService.findAthletById(id);
+        if (!athletOptional.isPresent()) {
+            return ResponseEntity.notFound().build();
+        }
+        
+        Athlet athlet = athletOptional.get();
+        athlet.setFullname(athletDetails.getFullname());
+        athlet.setEmail(athletDetails.getEmail());
+        // Passwort sollte separat geupdated werden
+        
+        Athlet updatedAthlet = athletService.saveAthlet(athlet);
+        return ResponseEntity.ok(updatedAthlet);
+    }
+    
+    @PutMapping("/{id}/password")
+    public ResponseEntity<?> updatePassword(@PathVariable Long id, 
+                                           @RequestParam String currentPassword,
+                                           @RequestParam String newPassword) {
+        Optional<Athlet> athletOptional = athletService.findAthletById(id);
+        if (!athletOptional.isPresent()) {
+            return ResponseEntity.notFound().build();
+        }
+        
+        boolean success = athletService.updatePassword(id, currentPassword, newPassword);
+        if (success) {
+            return ResponseEntity.ok().build();
+        } else {
+            return ResponseEntity.badRequest().body("Current password is incorrect");
+        }
+    }
+    
+    @DeleteMapping("/{id}")
+    public ResponseEntity<Void> deleteAthlet(@PathVariable Long id) {
+        if (!athletService.findAthletById(id).isPresent()) {
+            return ResponseEntity.notFound().build();
+        }
+        athletService.deleteAthlet(id);
+        return ResponseEntity.noContent().build();
+    }
+    
+    @PostMapping("/{id}/deactivate")
+    public ResponseEntity<Void> deactivateAthlet(@PathVariable Long id) {
+        athletService.deactivateAthlet(id);
+        return ResponseEntity.ok().build();
+    }
+    
+    @PostMapping("/{id}/activate")
+    public ResponseEntity<Void> activateAthlet(@PathVariable Long id) {
+        athletService.activateAthlet(id);
+        return ResponseEntity.ok().build();
+    }
+    
+    @GetMapping("/profile")
+    public ResponseEntity<Athlet> getProfile(HttpSession session) {
+        Athlet athlet = (Athlet) session.getAttribute("athlet");
+        if (athlet == null) {
+            return ResponseEntity.status(401).build();
+        }
+        return ResponseEntity.ok(athlet);
+    }
+    
+    @PutMapping("/profile")
+    public ResponseEntity<Athlet> updateProfile(@Valid @RequestBody Athlet athletDetails, HttpSession session) {
+        Athlet currentAthlet = (Athlet) session.getAttribute("athlet");
+        if (currentAthlet == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        currentAthlet.setFullname(athletDetails.getFullname());
+        currentAthlet.setEmail(athletDetails.getEmail());
+        
+        Athlet updatedAthlet = athletService.saveAthlet(currentAthlet);
+        session.setAttribute("athlet", updatedAthlet);
+        return ResponseEntity.ok(updatedAthlet);
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/AuthController.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/AuthController.java
new file mode 100644 (file)
index 0000000..f1c8bbe
--- /dev/null
@@ -0,0 +1,45 @@
+package com.triathlon_coaching.product.athletik.backend.contoller;
+
+import com.triathlon_coaching.product.athletik.backend.model.Athlet;
+import com.triathlon_coaching.product.athletik.backend.service.AthletService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpSession;
+import java.util.Optional;
+
+@RestController
+@RequestMapping("/api/auth")
+public class AuthController {
+    
+    @Autowired
+    private AthletService athletService;
+    
+    @PostMapping("/login")
+    public ResponseEntity<?> login(@RequestParam String username, 
+                                  @RequestParam String password,
+                                  HttpSession session) {
+        Optional<Athlet> athlet = athletService.authenticate(username, password);
+        if (athlet.isPresent()) {
+            session.setAttribute("athlet", athlet.get());
+            return ResponseEntity.ok(athlet.get());
+        }
+        return ResponseEntity.status(401).body("Ungültige Anmeldedaten");
+    }
+    
+    @PostMapping("/register")
+    public ResponseEntity<?> register(@RequestBody Athlet athlet) {
+        Optional<Athlet> newAthlet = athletService.registerAthlet(athlet);
+        if (newAthlet.isPresent()) {
+            return ResponseEntity.ok(newAthlet.get());
+        }
+        return ResponseEntity.badRequest().body("Benutzername oder E-Mail bereits vergeben");
+    }
+    
+    @PostMapping("/logout")
+    public ResponseEntity<?> logout(HttpSession session) {
+        session.invalidate();
+        return ResponseEntity.ok("Abgemeldet");
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/ExerciseController.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/ExerciseController.java
new file mode 100644 (file)
index 0000000..f00dbc7
--- /dev/null
@@ -0,0 +1,74 @@
+package com.triathlon_coaching.product.athletik.backend.contoller;
+
+import com.triathlon_coaching.product.athletik.backend.model.Exercise;
+import com.triathlon_coaching.product.athletik.backend.model.Muscle;
+import com.triathlon_coaching.product.athletik.backend.service.ExerciseService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/exercises")
+public class ExerciseController {
+
+    @Autowired
+    private ExerciseService exerciseService;
+
+    @GetMapping
+    public ResponseEntity<List<Exercise>> getAllExercises() {
+        List<Exercise> exercises = exerciseService.findAllExercises();
+        return ResponseEntity.ok(exercises);
+    }
+
+    @GetMapping("/{id}")
+    public ResponseEntity<Exercise> getExerciseById(@PathVariable Long id) {
+        return exerciseService.findExerciseById(id)
+                .map(ResponseEntity::ok)
+                .orElse(ResponseEntity.notFound().build());
+    }
+
+    @GetMapping("/musclegroup/{muscleGroup}")
+    public ResponseEntity<List<Exercise>> getExercisesByMuscleGroup(@PathVariable String muscleGroup) {
+        List<Exercise> exercises = exerciseService.findExercisesByMuscleGroup(muscleGroup);
+        return ResponseEntity.ok(exercises);
+    }
+
+    @GetMapping("/muscle/{muscleId}")
+    public ResponseEntity<List<Exercise>> getExercisesByMuscleId(@PathVariable Long muscleId) {
+        List<Exercise> exercises = exerciseService.findExercisesByMuscleId(muscleId);
+        return ResponseEntity.ok(exercises);
+    }
+
+    @GetMapping("/musclegroups")
+    public ResponseEntity<List<String>> getAllMuscleGroups() {
+        List<String> muscleGroups = exerciseService.findAllMuscleGroups();
+        return ResponseEntity.ok(muscleGroups);
+    }
+
+    @GetMapping("/muscles")
+    public ResponseEntity<List<Muscle>> getAllMuscles() {
+        List<Muscle> muscles = exerciseService.findAllMuscles();
+        return ResponseEntity.ok(muscles);
+    }
+
+    @GetMapping("/muscles/group/{muscleGroup}")
+    public ResponseEntity<List<Muscle>> getMusclesByGroup(@PathVariable String muscleGroup) {
+        List<Muscle> muscles = exerciseService.findMusclesByGroup(muscleGroup);
+        return ResponseEntity.ok(muscles);
+    }
+
+    @GetMapping("/tools/weights/{toolId}")
+    public ResponseEntity<List<Double>> getWeightsForTool(@PathVariable Long toolId) {
+        List<Double> weights = exerciseService.getWeightsForTool(toolId);
+        return ResponseEntity.ok(weights);
+    }
+
+    @GetMapping("/summary")
+    public ResponseEntity<Map<String, Object>> getExerciseSummary() {
+        Map<String, Object> summary = exerciseService.getExerciseSummary();
+        return ResponseEntity.ok(summary);
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/GoalController.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/GoalController.java
new file mode 100644 (file)
index 0000000..4660628
--- /dev/null
@@ -0,0 +1,96 @@
+package com.triathlon_coaching.product.athletik.backend.contoller;
+
+import com.triathlon_coaching.product.athletik.backend.model.Goal;
+import com.triathlon_coaching.product.athletik.backend.service.GoalService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Optional;
+
+@RestController
+@RequestMapping("/api/goals")
+public class GoalController {
+    
+    @Autowired
+    private GoalService goalService;
+    
+    @GetMapping
+    public ResponseEntity<List<Goal>> getAllGoals() {
+        List<Goal> goals = goalService.findAllGoals();
+        return ResponseEntity.ok(goals);
+    }
+    
+    @GetMapping("/{id}")
+    public ResponseEntity<Goal> getGoalById(@PathVariable Long id) {
+        Optional<Goal> goal = goalService.findGoalById(id);
+        return goal.map(ResponseEntity::ok)
+                  .orElse(ResponseEntity.notFound().build());
+    }
+    
+    @GetMapping("/name/{goalname}")
+    public ResponseEntity<Goal> getGoalByName(@PathVariable String goalname) {
+        Optional<Goal> goal = goalService.findGoalByName(goalname);
+        return goal.map(ResponseEntity::ok)
+                  .orElse(ResponseEntity.notFound().build());
+    }
+    
+    @GetMapping("/athlet/{athletId}")
+    public ResponseEntity<List<Goal>> getGoalsByAthletId(@PathVariable Long athletId) {
+        List<Goal> goals = goalService.findGoalsByAthletId(athletId);
+        return ResponseEntity.ok(goals);
+    }
+    
+    @GetMapping("/exercise/{exerciseId}")
+    public ResponseEntity<List<Goal>> getGoalsByExerciseId(@PathVariable Long exerciseId) {
+        List<Goal> goals = goalService.findGoalsByExerciseId(exerciseId);
+        return ResponseEntity.ok(goals);
+    }
+    
+    @GetMapping("/available/athlet/{athletId}")
+    public ResponseEntity<List<Goal>> getAvailableGoalsForAthlet(@PathVariable Long athletId) {
+        List<Goal> goals = goalService.findAvailableGoalsForAthlet(athletId);
+        return ResponseEntity.ok(goals);
+    }
+    
+    @GetMapping("/search")
+    public ResponseEntity<List<Goal>> searchGoals(@RequestParam String q) {
+        List<Goal> goals = goalService.searchGoals(q);
+        return ResponseEntity.ok(goals);
+    }
+    
+    @PostMapping
+    public ResponseEntity<Goal> createGoal(@Valid @RequestBody Goal goal) {
+        if (goalService.existsByName(goal.getGoalname())) {
+            return ResponseEntity.badRequest().build();
+        }
+        Goal savedGoal = goalService.saveGoal(goal);
+        return ResponseEntity.ok(savedGoal);
+    }
+    
+    @PutMapping("/{id}")
+    public ResponseEntity<Goal> updateGoal(@PathVariable Long id, @Valid @RequestBody Goal goalDetails) {
+        Optional<Goal> goalOptional = goalService.findGoalById(id);
+        if (!goalOptional.isPresent()) {
+            return ResponseEntity.notFound().build();
+        }
+        
+        Goal goal = goalOptional.get();
+        goal.setGoalname(goalDetails.getGoalname());
+        goal.setDescription(goalDetails.getDescription());
+        
+        Goal updatedGoal = goalService.saveGoal(goal);
+        return ResponseEntity.ok(updatedGoal);
+    }
+    
+    @DeleteMapping("/{id}")
+    public ResponseEntity<Void> deleteGoal(@PathVariable Long id) {
+        if (!goalService.findGoalById(id).isPresent()) {
+            return ResponseEntity.notFound().build();
+        }
+        goalService.deleteGoal(id);
+        return ResponseEntity.noContent().build();
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/GymController.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/GymController.java
new file mode 100644 (file)
index 0000000..c6644a3
--- /dev/null
@@ -0,0 +1,57 @@
+package com.triathlon_coaching.product.athletik.backend.contoller;
+
+import java.util.List;
+import javax.servlet.http.HttpSession;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.triathlon_coaching.product.athletik.backend.model.Gym;
+import com.triathlon_coaching.product.athletik.backend.service.GymService;
+
+@RestController
+@RequestMapping("/api/gyms")
+public class GymController {
+    
+    @Autowired
+    private GymService gymService;
+    
+    @GetMapping
+    public ResponseEntity<List<Gym>> getAllGyms() {
+        List<Gym> gyms = gymService.findAllGyms();
+        return ResponseEntity.ok(gyms);
+    }
+    
+    @GetMapping("/{id}")
+    public ResponseEntity<Gym> getGymById(@PathVariable Long id) {
+        return gymService.findGymById(id)
+                .map(ResponseEntity::ok)
+                .orElse(ResponseEntity.notFound().build());
+    }
+    
+    @GetMapping("/athlet/{athletId}")
+    public ResponseEntity<List<Gym>> getGymsByAthletId(@PathVariable Long athletId) {
+        List<Gym> gyms = gymService.findGymsByAthletId(athletId);
+        return ResponseEntity.ok(gyms);
+    }
+    
+    @PostMapping("/{gymId}/select")
+    public ResponseEntity<?> selectGym(@PathVariable Long gymId, HttpSession session) {
+        // Setzt das ausgewählte Gym in der Session
+        gymService.findGymById(gymId).ifPresent(gym -> {
+            session.setAttribute("selectedGym", gym);
+        });
+        return ResponseEntity.ok().build();
+    }
+    
+    @GetMapping("/selected")
+    public ResponseEntity<Gym> getSelectedGym(HttpSession session) {
+        Gym selectedGym = (Gym) session.getAttribute("selectedGym");
+        return ResponseEntity.ok(selectedGym);
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/LogController.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/LogController.java
new file mode 100644 (file)
index 0000000..eda57b1
--- /dev/null
@@ -0,0 +1,145 @@
+package com.triathlon_coaching.product.athletik.backend.contoller;
+
+import com.triathlon_coaching.product.athletik.backend.model.Log;
+import com.triathlon_coaching.product.athletik.backend.service.LogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpSession;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/logs")
+public class LogController {
+    
+    @Autowired
+    private LogService logService;
+    
+    @GetMapping
+    public ResponseEntity<List<Log>> getAllLogs(HttpSession session) {
+        // Nur Admin-Zugriff erlauben
+        // Hier vereinfacht - in Produktion sollte eine richtige Autorisierung implementiert werden
+        return ResponseEntity.ok(logService.findAllLogs());
+    }
+    
+    @GetMapping("/my-logs")
+    public ResponseEntity<List<Log>> getMyLogs(HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        List<Log> logs = logService.findByAthletId(athletId);
+        return ResponseEntity.ok(logs);
+    }
+    
+    @GetMapping("/my-logs/paged")
+    public ResponseEntity<Page<Log>> getMyLogsPaged(
+            HttpSession session,
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "20") int size) {
+        
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        Pageable pageable = PageRequest.of(page, size, Sort.by("created").descending());
+        Page<Log> logs = logService.findByAthletId(athletId, pageable);
+        return ResponseEntity.ok(logs);
+    }
+    
+    @GetMapping("/search")
+    public ResponseEntity<List<Log>> searchLogs(
+            HttpSession session,
+            @RequestParam(required = false) String level,
+            @RequestParam(required = false) String searchTerm,
+            @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDate,
+            @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDate) {
+        
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        List<Log> logs;
+        if (level != null) {
+            logs = logService.findByAthletIdAndLogLevel(athletId, level);
+        } else if (searchTerm != null) {
+            logs = logService.searchByAthletIdAndLogdata(athletId, searchTerm);
+        } else if (startDate != null && endDate != null) {
+            logs = logService.findByAthletIdAndDateRange(athletId, startDate, endDate);
+        } else {
+            logs = logService.findByAthletId(athletId);
+        }
+        
+        return ResponseEntity.ok(logs);
+    }
+    
+    @GetMapping("/stats")
+    public ResponseEntity<?> getLogStats(HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        LocalDateTime last24Hours = LocalDateTime.now().minusHours(24);
+        Long countLast24Hours = logService.getLogCountSince(athletId, last24Hours);
+        
+        List<String> scripts = logService.getDistinctScripts(athletId);
+        List<String> levels = logService.getDistinctLogLevels(athletId);
+        Log latestLog = logService.getLatestLog(athletId);
+        
+        return ResponseEntity.ok(new LogStats(countLast24Hours, scripts, levels, latestLog));
+    }
+    
+    @DeleteMapping("/{id}")
+    public ResponseEntity<Void> deleteLog(@PathVariable Long id, HttpSession session) {
+        // Prüfen, ob der Benutzer berechtigt ist, diesen Log zu löschen
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        // Hier sollte zusätzlich geprüft werden, ob der Log zum Benutzer gehört
+        logService.deleteLog(id);
+        return ResponseEntity.noContent().build();
+    }
+    
+    // Hilfsmethode zur Extraktion der Athlet-ID aus der Session
+    private Long getAthletIdFromSession(HttpSession session) {
+        Object athletObj = session.getAttribute("athlet");
+        if (athletObj instanceof com.triathlon_coaching.product.athletik.backend.model.Athlet) {
+            return ((com.triathlon_coaching.product.athletik.backend.model.Athlet) athletObj).getId();
+        }
+        return null;
+    }
+    
+    // DTO für Log-Statistiken
+    private static class LogStats {
+        private Long countLast24Hours;
+        private List<String> scripts;
+        private List<String> levels;
+        private Log latestLog;
+        
+        public LogStats(Long countLast24Hours, List<String> scripts, List<String> levels, Log latestLog) {
+            this.countLast24Hours = countLast24Hours;
+            this.scripts = scripts;
+            this.levels = levels;
+            this.latestLog = latestLog;
+        }
+        
+        // Getter
+        public Long getCountLast24Hours() { return countLast24Hours; }
+        public List<String> getScripts() { return scripts; }
+        public List<String> getLevels() { return levels; }
+        public Log getLatestLog() { return latestLog; }
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/MuscleController.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/MuscleController.java
new file mode 100644 (file)
index 0000000..1b97002
--- /dev/null
@@ -0,0 +1,54 @@
+package com.triathlon_coaching.product.athletik.backend.contoller;
+
+import com.triathlon_coaching.product.athletik.backend.model.Muscle;
+import com.triathlon_coaching.product.athletik.backend.service.MuscleService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/muscles")
+public class MuscleController {
+    
+    @Autowired
+    private MuscleService muscleService;
+    
+    @GetMapping
+    public ResponseEntity<List<Muscle>> getAllMuscles() {
+        List<Muscle> muscles = muscleService.findAllMuscles();
+        return ResponseEntity.ok(muscles);
+    }
+    
+    @GetMapping("/{id}")
+    public ResponseEntity<Muscle> getMuscleById(@PathVariable Long id) {
+        return muscleService.findMuscleById(id)
+                .map(ResponseEntity::ok)
+                .orElse(ResponseEntity.notFound().build());
+    }
+    
+    @GetMapping("/group/{muscleGroup}")
+    public ResponseEntity<List<Muscle>> getMusclesByGroup(@PathVariable String muscleGroup) {
+        List<Muscle> muscles = muscleService.findMusclesByGroup(muscleGroup);
+        return ResponseEntity.ok(muscles);
+    }
+    
+    @GetMapping("/groups")
+    public ResponseEntity<List<String>> getAllMuscleGroups() {
+        List<String> muscleGroups = muscleService.findAllMuscleGroups();
+        return ResponseEntity.ok(muscleGroups);
+    }
+    
+    @GetMapping("/exercise/{exerciseId}")
+    public ResponseEntity<List<Muscle>> getMusclesByExerciseId(@PathVariable Long exerciseId) {
+        List<Muscle> muscles = muscleService.findMusclesByExerciseId(exerciseId);
+        return ResponseEntity.ok(muscles);
+    }
+    
+    @GetMapping("/primary/exercise/{exerciseId}")
+    public ResponseEntity<List<Muscle>> getPrimaryMusclesByExerciseId(@PathVariable Long exerciseId) {
+        List<Muscle> muscles = muscleService.findPrimaryMusclesByExerciseId(exerciseId);
+        return ResponseEntity.ok(muscles);
+    }
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/ProtokollController.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/ProtokollController.java
new file mode 100644 (file)
index 0000000..f2961a0
--- /dev/null
@@ -0,0 +1,252 @@
+package com.triathlon_coaching.product.athletik.backend.contoller;
+
+import com.triathlon_coaching.product.athletik.backend.model.Protokoll;
+import com.triathlon_coaching.product.athletik.backend.service.ProtokollService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpSession;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+@RestController
+@RequestMapping("/api/protokoll")
+public class ProtokollController {
+    
+    @Autowired
+    private ProtokollService protokollService;
+    
+    @GetMapping
+    public ResponseEntity<List<Protokoll>> getAllProtokolls() {
+        List<Protokoll> protokolls = protokollService.findAllProtokolls();
+        return ResponseEntity.ok(protokolls);
+    }
+    
+    @GetMapping("/{id}")
+    public ResponseEntity<Protokoll> getProtokollById(@PathVariable Long id) {
+        Optional<Protokoll> protokoll = protokollService.findProtokollById(id);
+        return protokoll.map(ResponseEntity::ok)
+                       .orElse(ResponseEntity.notFound().build());
+    }
+    
+    @GetMapping("/my-protokolls")
+    public ResponseEntity<List<Protokoll>> getMyProtokolls(HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        List<Protokoll> protokolls = protokollService.findByAthletId(athletId);
+        return ResponseEntity.ok(protokolls);
+    }
+    
+    @GetMapping("/exercise/{exerciseId}")
+    public ResponseEntity<List<Protokoll>> getProtokollsByExercise(
+            @PathVariable Long exerciseId, HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        List<Protokoll> protokolls = protokollService.findByAthletIdAndExerciseId(athletId, exerciseId);
+        return ResponseEntity.ok(protokolls);
+    }
+    
+    @GetMapping("/exercise/{exerciseId}/paged")
+    public ResponseEntity<Page<Protokoll>> getProtokollsByExercisePaged(
+            @PathVariable Long exerciseId,
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "10") int size,
+            HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        Pageable pageable = PageRequest.of(page, size, Sort.by("trainingDate").descending());
+        Page<Protokoll> protokolls = protokollService.findByAthletIdAndExerciseIdPaged(athletId, exerciseId, pageable);
+        return ResponseEntity.ok(protokolls);
+    }
+    
+    @GetMapping("/today")
+    public ResponseEntity<List<Protokoll>> getTodaySessions(HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        List<Protokoll> todaySessions = protokollService.findTodaySessionsByAthletId(athletId);
+        return ResponseEntity.ok(todaySessions);
+    }
+    
+    @GetMapping("/date-range")
+    public ResponseEntity<List<Protokoll>> getProtokollsByDateRange(
+            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start,
+            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end,
+            HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        List<Protokoll> protokolls = protokollService.findByAthletIdAndDateRange(athletId, start, end);
+        return ResponseEntity.ok(protokolls);
+    }
+    
+    @GetMapping("/tests")
+    public ResponseEntity<List<Protokoll>> getMyTests(HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        List<Protokoll> tests = protokollService.findTestsByAthletId(athletId);
+        return ResponseEntity.ok(tests);
+    }
+    
+    @GetMapping("/tests/exercise/{exerciseId}")
+    public ResponseEntity<List<Protokoll>> getTestsByExercise(
+            @PathVariable Long exerciseId, HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        List<Protokoll> tests = protokollService.findLatestTestsByAthletAndExercise(athletId, exerciseId);
+        return ResponseEntity.ok(tests);
+    }
+    
+    @GetMapping("/personal-records")
+    public ResponseEntity<List<Protokoll>> getPersonalRecords(HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        List<Protokoll> records = protokollService.findPersonalRecords(athletId);
+        return ResponseEntity.ok(records);
+    }
+    
+    @GetMapping("/stats/volume")
+    public ResponseEntity<VolumeStats> getVolumeStats(HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        Double dailyVolume = protokollService.calculateDailyVolume(athletId);
+        Double weeklyVolume = protokollService.calculateWeeklyVolume(athletId);
+        
+        return ResponseEntity.ok(new VolumeStats(dailyVolume, weeklyVolume));
+    }
+    
+    @PostMapping
+    public ResponseEntity<Protokoll> createProtokoll(@RequestBody Protokoll protokoll, HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        // Stellen Sie sicher, dass der Protokoll-Eintrag zum eingeloggten Athleten gehört
+        if (protokoll.getAthlet() == null || !protokoll.getAthlet().getId().equals(athletId)) {
+            return ResponseEntity.status(403).build();
+        }
+        
+        Protokoll savedProtokoll = protokollService.saveProtokoll(protokoll);
+        return ResponseEntity.ok(savedProtokoll);
+    }
+    
+    @PutMapping("/{id}")
+    public ResponseEntity<Protokoll> updateProtokoll(@PathVariable Long id, @RequestBody Protokoll protokollDetails, HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        Optional<Protokoll> protokollOptional = protokollService.findProtokollById(id);
+        if (!protokollOptional.isPresent()) {
+            return ResponseEntity.notFound().build();
+        }
+        
+        Protokoll protokoll = protokollOptional.get();
+        // Prüfen, ob der Protokoll-Eintrag zum eingeloggten Athleten gehört
+        if (!protokoll.getAthlet().getId().equals(athletId)) {
+            return ResponseEntity.status(403).build();
+        }
+        
+        // Aktualisieren der Felder
+        protokoll.setTrainingDate(protokollDetails.getTrainingDate());
+        protokoll.setNumberOfSet(protokollDetails.getNumberOfSet());
+        protokoll.setRepetitions(protokollDetails.getRepetitions());
+        protokoll.setWeight(protokollDetails.getWeight());
+        protokoll.setIsTest(protokollDetails.getIsTest());
+        protokoll.setNotes(protokollDetails.getNotes());
+        protokoll.setRpe(protokollDetails.getRpe());
+        protokoll.setRestTime(protokollDetails.getRestTime());
+        
+        Protokoll updatedProtokoll = protokollService.saveProtokoll(protokoll);
+        return ResponseEntity.ok(updatedProtokoll);
+    }
+    
+    @DeleteMapping("/{id}")
+    public ResponseEntity<Void> deleteProtokoll(@PathVariable Long id, HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        Optional<Protokoll> protokollOptional = protokollService.findProtokollById(id);
+        if (!protokollOptional.isPresent()) {
+            return ResponseEntity.notFound().build();
+        }
+        
+        // Prüfen, ob der Protokoll-Eintrag zum eingeloggten Athleten gehört
+        if (!protokollOptional.get().getAthlet().getId().equals(athletId)) {
+            return ResponseEntity.status(403).build();
+        }
+        
+        protokollService.deleteProtokoll(id);
+        return ResponseEntity.noContent().build();
+    }
+    
+    @PostMapping("/send-summary")
+    public ResponseEntity<Void> sendTrainingSummary(HttpSession session) {
+        Long athletId = getAthletIdFromSession(session);
+        if (athletId == null) {
+            return ResponseEntity.status(401).build();
+        }
+        
+        protokollService.sendTrainingSummaryEmail(athletId);
+        return ResponseEntity.ok().build();
+    }
+    
+    // Hilfsmethode zur Extraktion der Athlet-ID aus der Session
+    private Long getAthletIdFromSession(HttpSession session) {
+        Object athletObj = session.getAttribute("athlet");
+        if (athletObj instanceof com.triathlon_coaching.product.athletik.backend.model.Athlet) {
+            return ((com.triathlon_coaching.product.athletik.backend.model.Athlet) athletObj).getId();
+        }
+        return null;
+    }
+    
+    // DTO für Volumen-Statistiken
+    private static class VolumeStats {
+        private Double dailyVolume;
+        private Double weeklyVolume;
+        
+        public VolumeStats(Double dailyVolume, Double weeklyVolume) {
+            this.dailyVolume = dailyVolume;
+            this.weeklyVolume = weeklyVolume;
+        }
+        
+        public Double getDailyVolume() { return dailyVolume; }
+        public Double getWeeklyVolume() { return weeklyVolume; }
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/ToolController.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/ToolController.java
new file mode 100644 (file)
index 0000000..13a5d62
--- /dev/null
@@ -0,0 +1,88 @@
+package com.triathlon_coaching.product.athletik.backend.contoller;
+
+import com.triathlon_coaching.product.athletik.backend.model.Tool;
+import com.triathlon_coaching.product.athletik.backend.service.ToolService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Set;
+
+@RestController
+@RequestMapping("/api/tools")
+public class ToolController {
+    
+    @Autowired
+    private ToolService toolService;
+    
+    @GetMapping
+    public ResponseEntity<List<Tool>> getAllTools() {
+        List<Tool> tools = toolService.findAllTools();
+        return ResponseEntity.ok(tools);
+    }
+    
+    @GetMapping("/{id}")
+    public ResponseEntity<Tool> getToolById(@PathVariable Long id) {
+        return toolService.findToolById(id)
+                .map(ResponseEntity::ok)
+                .orElse(ResponseEntity.notFound().build());
+    }
+    
+    @GetMapping("/gym/{gymId}")
+    public ResponseEntity<List<Tool>> getToolsByGymId(@PathVariable Long gymId) {
+        List<Tool> tools = toolService.findToolsByGymId(gymId);
+        return ResponseEntity.ok(tools);
+    }
+    
+    @GetMapping("/{id}/weights")
+    public ResponseEntity<Set<Double>> getAvailableWeights(@PathVariable Long id) {
+        return toolService.findToolById(id)
+                .map(tool -> ResponseEntity.ok(tool.getAvailableWeights()))
+                .orElse(ResponseEntity.notFound().build());
+    }
+    
+    @GetMapping("/search")
+    public ResponseEntity<List<Tool>> searchTools(
+            @RequestParam(required = false) String name,
+            @RequestParam(required = false) String language) {
+        
+        List<Tool> tools;
+        if (name != null && !name.trim().isEmpty()) {
+            if ("german".equalsIgnoreCase(language)) {
+                tools = toolService.findByToolnameGermanContainingIgnoreCase(name);
+            } else {
+                tools = toolService.findByToolnameEnglishContainingIgnoreCase(name);
+            }
+        } else {
+            tools = toolService.findAllTools();
+        }
+        
+        return ResponseEntity.ok(tools);
+    }
+    
+    @PostMapping
+    public ResponseEntity<Tool> createTool(@RequestBody Tool tool) {
+        Tool savedTool = toolService.saveTool(tool);
+        return ResponseEntity.ok(savedTool);
+    }
+    
+    @PutMapping("/{id}")
+    public ResponseEntity<Tool> updateTool(@PathVariable Long id, @RequestBody Tool tool) {
+        if (!toolService.findToolById(id).isPresent()) {
+            return ResponseEntity.notFound().build();
+        }
+        tool.setId(id);
+        Tool updatedTool = toolService.saveTool(tool);
+        return ResponseEntity.ok(updatedTool);
+    }
+    
+    @DeleteMapping("/{id}")
+    public ResponseEntity<Void> deleteTool(@PathVariable Long id) {
+        if (!toolService.findToolById(id).isPresent()) {
+            return ResponseEntity.notFound().build();
+        }
+        toolService.deleteTool(id);
+        return ResponseEntity.noContent().build();
+    }
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/TrainingController.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/contoller/TrainingController.java
new file mode 100644 (file)
index 0000000..f08ef84
--- /dev/null
@@ -0,0 +1,40 @@
+package com.triathlon_coaching.product.athletik.backend.contoller;
+
+import javax.servlet.http.HttpSession;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.triathlon_coaching.product.athletik.backend.model.Athlet;
+import com.triathlon_coaching.product.athletik.backend.service.TrainingService;
+
+@RestController
+@RequestMapping("/api/training")
+public class TrainingController {
+    
+    @Autowired
+    private TrainingService trainingService;
+    
+   
+    @PostMapping("/send-summary")
+    public ResponseEntity<?> sendTrainingSummary(HttpSession session) {
+        Athlet athlet = (Athlet) session.getAttribute("athlet");
+        if (athlet == null) {
+            return ResponseEntity.status(401).build();
+        }
+        trainingService.sendTrainingSummaryEmail(athlet.getId());
+        return ResponseEntity.ok("Zusammenfassung wurde per E-Mail versendet");
+    }
+    
+    @GetMapping("/calculate-orm")
+    public ResponseEntity<Double> calculateOneRepMax(@RequestParam double weight, 
+                                                    @RequestParam int repetitions) {
+        double oneRepMax = trainingService.calculateOneRepMax(weight, repetitions);
+        return ResponseEntity.ok(oneRepMax);
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Athlet.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Athlet.java
new file mode 100644 (file)
index 0000000..cbe960c
--- /dev/null
@@ -0,0 +1,380 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.time.LocalDateTime;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinTable;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToMany;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "athlet", schema = "athletik")
+public class Athlet {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    
+    @Column(nullable = false, unique = true, length = 50)
+    private String username;
+    
+    @Column(name = "fullname", nullable = false, length = 100)
+    private String fullname;
+    
+    @Column(nullable = false, unique = true, length = 100)
+    private String email;
+    
+    @Column(nullable = false, length = 128)
+    private String pass;
+    
+    @Column(name = "created_at", updatable = false)
+    private LocalDateTime createdAt;
+    
+    @Column(name = "last_login")
+    private LocalDateTime lastLogin;
+    
+    @Column(name = "is_active")
+    private Boolean isActive = true;
+    
+    @ManyToMany
+    @JoinTable(
+        name = "athletgym", 
+        schema = "athletik",
+        joinColumns = @JoinColumn(name = "fk_athlet"), 
+        inverseJoinColumns = @JoinColumn(name = "fk_gym")
+    )
+    private Set<Gym> gyms = new HashSet<>();
+    
+    @OneToMany(mappedBy = "athlet", cascade = CascadeType.ALL, orphanRemoval = true)
+    private Set<Protokoll> protokolls = new HashSet<>();
+    
+    @OneToMany(mappedBy = "athlet", cascade = CascadeType.ALL, orphanRemoval = true)
+    private Set<Training> trainings = new HashSet<>();
+    
+    @OneToMany(mappedBy = "athlet", cascade = CascadeType.ALL, orphanRemoval = true)
+    private Set<Log> logs = new HashSet<>();
+
+       public Athlet() {
+               super();
+               // TODO Auto-generated constructor stub
+       }
+
+
+       public Athlet(Long id, String username, String fullname, String email, String pass, LocalDateTime createdAt,
+                       LocalDateTime lastLogin, Boolean isActive, Set<Gym> gyms, Set<Protokoll> protokolls,
+                       Set<Training> trainings, Set<Log> logs) {
+               super();
+               this.username = username;
+               this.fullname = fullname;
+               this.email = email;
+               this.pass = pass;
+               this.createdAt = createdAt;
+               this.lastLogin = lastLogin;
+               this.isActive = isActive;
+               this.gyms = gyms;
+               this.protokolls = protokolls;
+               this.trainings = trainings;
+               this.logs = logs;
+       }
+
+       /**
+        * @return the id
+        */
+       public Long getId() {
+               return id;
+       }
+
+       /**
+        * @param id the id to set
+        */
+       public void setId(Long id) {
+               this.id = id;
+       }
+
+       /**
+        * @return the username
+        */
+       public String getUsername() {
+               return username;
+       }
+
+       /**
+        * @param username the username to set
+        */
+       public void setUsername(String username) {
+               this.username = username;
+       }
+
+       /**
+        * @return the fullname
+        */
+       public String getFullname() {
+               return fullname;
+       }
+
+       /**
+        * @param fullname the fullname to set
+        */
+       public void setFullname(String fullname) {
+               this.fullname = fullname;
+       }
+
+       /**
+        * @return the email
+        */
+       public String getEmail() {
+               return email;
+       }
+
+       /**
+        * @param email the email to set
+        */
+       public void setEmail(String email) {
+               this.email = email;
+       }
+
+       /**
+        * @return the pass
+        */
+       public String getPass() {
+               return pass;
+       }
+
+       /**
+        * @param pass the pass to set
+        */
+       public void setPass(String pass) {
+               this.pass = pass;
+       }
+
+       /**
+        * @return the gyms
+        */
+       public Set<Gym> getGyms() {
+               return gyms;
+       }
+
+       /**
+        * @param gyms the gyms to set
+        */
+       public void setGyms(Set<Gym> gyms) {
+               this.gyms = gyms;
+       }
+
+       /**
+        * @return the createdAt
+        */
+       public LocalDateTime getCreatedAt() {
+               return createdAt;
+       }
+
+       /**
+        * @param createdAt the createdAt to set
+        */
+       public void setCreatedAt(LocalDateTime createdAt) {
+               this.createdAt = createdAt;
+       }
+
+       /**
+        * @return the lastLogin
+        */
+       public LocalDateTime getLastLogin() {
+               return lastLogin;
+       }
+
+       /**
+        * @param lastLogin the lastLogin to set
+        */
+       public void setLastLogin(LocalDateTime lastLogin) {
+               this.lastLogin = lastLogin;
+       }
+
+       /**
+        * @return the isActive
+        */
+       public Boolean getIsActive() {
+               return isActive;
+       }
+
+       /**
+        * @param isActive the isActive to set
+        */
+       public void setIsActive(Boolean isActive) {
+               this.isActive = isActive;
+       }
+
+       /**
+        * @return the protokolls
+        */
+       public Set<Protokoll> getProtokolls() {
+               return protokolls;
+       }
+
+       /**
+        * @param protokolls the protokolls to set
+        */
+       public void setProtokolls(Set<Protokoll> protokolls) {
+               this.protokolls = protokolls;
+       }
+
+       /**
+        * @return the trainings
+        */
+       public Set<Training> getTrainings() {
+               return trainings;
+       }
+
+       /**
+        * @param trainings the trainings to set
+        */
+       public void setTrainings(Set<Training> trainings) {
+               this.trainings = trainings;
+       }
+
+       /**
+        * @return the logs
+        */
+       public Set<Log> getLogs() {
+               return logs;
+       }
+
+       /**
+        * @param logs the logs to set
+        */
+       public void setLogs(Set<Log> logs) {
+               this.logs = logs;
+       }
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((createdAt == null) ? 0 : createdAt.hashCode());
+               result = prime * result + ((email == null) ? 0 : email.hashCode());
+               result = prime * result + ((fullname == null) ? 0 : fullname.hashCode());
+               result = prime * result + ((gyms == null) ? 0 : gyms.hashCode());
+               result = prime * result + ((id == null) ? 0 : id.hashCode());
+               result = prime * result + ((isActive == null) ? 0 : isActive.hashCode());
+               result = prime * result + ((lastLogin == null) ? 0 : lastLogin.hashCode());
+               result = prime * result + ((logs == null) ? 0 : logs.hashCode());
+               result = prime * result + ((pass == null) ? 0 : pass.hashCode());
+               result = prime * result + ((protokolls == null) ? 0 : protokolls.hashCode());
+               result = prime * result + ((trainings == null) ? 0 : trainings.hashCode());
+               result = prime * result + ((username == null) ? 0 : username.hashCode());
+               return result;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               Athlet other = (Athlet) obj;
+               if (createdAt == null) {
+                       if (other.createdAt != null)
+                               return false;
+               } else if (!createdAt.equals(other.createdAt))
+                       return false;
+               if (email == null) {
+                       if (other.email != null)
+                               return false;
+               } else if (!email.equals(other.email))
+                       return false;
+               if (fullname == null) {
+                       if (other.fullname != null)
+                               return false;
+               } else if (!fullname.equals(other.fullname))
+                       return false;
+               if (gyms == null) {
+                       if (other.gyms != null)
+                               return false;
+               } else if (!gyms.equals(other.gyms))
+                       return false;
+               if (id == null) {
+                       if (other.id != null)
+                               return false;
+               } else if (!id.equals(other.id))
+                       return false;
+               if (isActive == null) {
+                       if (other.isActive != null)
+                               return false;
+               } else if (!isActive.equals(other.isActive))
+                       return false;
+               if (lastLogin == null) {
+                       if (other.lastLogin != null)
+                               return false;
+               } else if (!lastLogin.equals(other.lastLogin))
+                       return false;
+               if (logs == null) {
+                       if (other.logs != null)
+                               return false;
+               } else if (!logs.equals(other.logs))
+                       return false;
+               if (pass == null) {
+                       if (other.pass != null)
+                               return false;
+               } else if (!pass.equals(other.pass))
+                       return false;
+               if (protokolls == null) {
+                       if (other.protokolls != null)
+                               return false;
+               } else if (!protokolls.equals(other.protokolls))
+                       return false;
+               if (trainings == null) {
+                       if (other.trainings != null)
+                               return false;
+               } else if (!trainings.equals(other.trainings))
+                       return false;
+               if (username == null) {
+                       if (other.username != null)
+                               return false;
+               } else if (!username.equals(other.username))
+                       return false;
+               return true;
+       }
+
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("Athlet [id=");
+               builder.append(id);
+               builder.append(", username=");
+               builder.append(username);
+               builder.append(", fullname=");
+               builder.append(fullname);
+               builder.append(", email=");
+               builder.append(email);
+               builder.append(", pass=");
+               builder.append(pass);
+               builder.append(", createdAt=");
+               builder.append(createdAt);
+               builder.append(", lastLogin=");
+               builder.append(lastLogin);
+               builder.append(", isActive=");
+               builder.append(isActive);
+               builder.append(", gyms=");
+               builder.append(gyms);
+               builder.append(", protokolls=");
+               builder.append(protokolls);
+               builder.append(", trainings=");
+               builder.append(trainings);
+               builder.append(", logs=");
+               builder.append(logs);
+               builder.append("]");
+               return builder.toString();
+       }
+
+    
+    
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Exercise.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Exercise.java
new file mode 100644 (file)
index 0000000..a48d4da
--- /dev/null
@@ -0,0 +1,273 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "exercise", schema = "athletik")
+public class Exercise {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    
+    @Column(name = "name_english", nullable = false)
+    private String nameEnglish;
+    
+    @Column(name = "description_english")
+    private String descriptionEnglish;
+    
+    @Column(name = "name_german")
+    private String nameGerman;
+    
+    @Column(name = "description_german")
+    private String descriptionGerman;
+    
+    @Column(name = "url_video")
+    private String urlVideo;
+    
+    @ManyToOne
+    @JoinColumn(name = "fk_tool", nullable = false)
+    private Tool tool;
+    
+    @ManyToMany
+    @JoinTable(
+        name = "exercise_muscle", 
+        schema = "athletik",
+        joinColumns = @JoinColumn(name = "fk_exercise"), 
+        inverseJoinColumns = @JoinColumn(name = "fk_muscle")
+    )
+    private Set<Muscle> muscles = new HashSet<>();
+
+       public Exercise() {
+               super();
+               // TODO Auto-generated constructor stub
+       }
+
+       public Exercise(String nameEnglish, String descriptionEnglish, String nameGerman, String descriptionGerman,
+                       String urlVideo, Tool tool, Set<Muscle> muscles) {
+               super();
+               this.nameEnglish = nameEnglish;
+               this.descriptionEnglish = descriptionEnglish;
+               this.nameGerman = nameGerman;
+               this.descriptionGerman = descriptionGerman;
+               this.urlVideo = urlVideo;
+               this.tool = tool;
+               this.muscles = muscles;
+       }
+
+       /**
+        * @return the id
+        */
+       public Long getId() {
+               return id;
+       }
+
+       /**
+        * @param id the id to set
+        */
+       public void setId(Long id) {
+               this.id = id;
+       }
+
+       /**
+        * @return the nameEnglish
+        */
+       public String getNameEnglish() {
+               return nameEnglish;
+       }
+
+       /**
+        * @param nameEnglish the nameEnglish to set
+        */
+       public void setNameEnglish(String nameEnglish) {
+               this.nameEnglish = nameEnglish;
+       }
+
+       /**
+        * @return the descriptionEnglish
+        */
+       public String getDescriptionEnglish() {
+               return descriptionEnglish;
+       }
+
+       /**
+        * @param descriptionEnglish the descriptionEnglish to set
+        */
+       public void setDescriptionEnglish(String descriptionEnglish) {
+               this.descriptionEnglish = descriptionEnglish;
+       }
+
+       /**
+        * @return the nameGerman
+        */
+       public String getNameGerman() {
+               return nameGerman;
+       }
+
+       /**
+        * @param nameGerman the nameGerman to set
+        */
+       public void setNameGerman(String nameGerman) {
+               this.nameGerman = nameGerman;
+       }
+
+       /**
+        * @return the descriptionGerman
+        */
+       public String getDescriptionGerman() {
+               return descriptionGerman;
+       }
+
+       /**
+        * @param descriptionGerman the descriptionGerman to set
+        */
+       public void setDescriptionGerman(String descriptionGerman) {
+               this.descriptionGerman = descriptionGerman;
+       }
+
+       /**
+        * @return the urlVideo
+        */
+       public String getUrlVideo() {
+               return urlVideo;
+       }
+
+       /**
+        * @param urlVideo the urlVideo to set
+        */
+       public void setUrlVideo(String urlVideo) {
+               this.urlVideo = urlVideo;
+       }
+
+       /**
+        * @return the tool
+        */
+       public Tool getTool() {
+               return tool;
+       }
+
+       /**
+        * @param tool the tool to set
+        */
+       public void setTool(Tool tool) {
+               this.tool = tool;
+       }
+
+       /**
+        * @return the muscles
+        */
+       public Set<Muscle> getMuscles() {
+               return muscles;
+       }
+
+       /**
+        * @param muscles the muscles to set
+        */
+       public void setMuscles(Set<Muscle> muscles) {
+               this.muscles = muscles;
+       }
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((descriptionEnglish == null) ? 0 : descriptionEnglish.hashCode());
+               result = prime * result + ((descriptionGerman == null) ? 0 : descriptionGerman.hashCode());
+               result = prime * result + ((id == null) ? 0 : id.hashCode());
+               result = prime * result + ((muscles == null) ? 0 : muscles.hashCode());
+               result = prime * result + ((nameEnglish == null) ? 0 : nameEnglish.hashCode());
+               result = prime * result + ((nameGerman == null) ? 0 : nameGerman.hashCode());
+               result = prime * result + ((tool == null) ? 0 : tool.hashCode());
+               result = prime * result + ((urlVideo == null) ? 0 : urlVideo.hashCode());
+               return result;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               Exercise other = (Exercise) obj;
+               if (descriptionEnglish == null) {
+                       if (other.descriptionEnglish != null)
+                               return false;
+               } else if (!descriptionEnglish.equals(other.descriptionEnglish))
+                       return false;
+               if (descriptionGerman == null) {
+                       if (other.descriptionGerman != null)
+                               return false;
+               } else if (!descriptionGerman.equals(other.descriptionGerman))
+                       return false;
+               if (id == null) {
+                       if (other.id != null)
+                               return false;
+               } else if (!id.equals(other.id))
+                       return false;
+               if (muscles == null) {
+                       if (other.muscles != null)
+                               return false;
+               } else if (!muscles.equals(other.muscles))
+                       return false;
+               if (nameEnglish == null) {
+                       if (other.nameEnglish != null)
+                               return false;
+               } else if (!nameEnglish.equals(other.nameEnglish))
+                       return false;
+               if (nameGerman == null) {
+                       if (other.nameGerman != null)
+                               return false;
+               } else if (!nameGerman.equals(other.nameGerman))
+                       return false;
+               if (tool == null) {
+                       if (other.tool != null)
+                               return false;
+               } else if (!tool.equals(other.tool))
+                       return false;
+               if (urlVideo == null) {
+                       if (other.urlVideo != null)
+                               return false;
+               } else if (!urlVideo.equals(other.urlVideo))
+                       return false;
+               return true;
+       }
+
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("Exercise [id=");
+               builder.append(id);
+               builder.append(", nameEnglish=");
+               builder.append(nameEnglish);
+               builder.append(", descriptionEnglish=");
+               builder.append(descriptionEnglish);
+               builder.append(", nameGerman=");
+               builder.append(nameGerman);
+               builder.append(", descriptionGerman=");
+               builder.append(descriptionGerman);
+               builder.append(", urlVideo=");
+               builder.append(urlVideo);
+               builder.append(", tool=");
+               builder.append(tool);
+               builder.append(", muscles=");
+               builder.append(muscles);
+               builder.append("]");
+               return builder.toString();
+       }
+    
+    
+    
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Goal.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Goal.java
new file mode 100644 (file)
index 0000000..5120f37
--- /dev/null
@@ -0,0 +1,158 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.HashSet;
+import java.util.Set;
+
+@Entity
+@Table(name = "goal", schema = "athletik")
+public class Goal {
+    
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    
+    @Column(name = "goalname", nullable = false, length = 100)
+    private String goalname;
+    
+    @Column(name = "description", columnDefinition = "TEXT")
+    private String description;
+    
+    @OneToMany(mappedBy = "goal", cascade = CascadeType.ALL, orphanRemoval = true)
+    private Set<Training> trainings = new HashSet<>();
+
+       public Goal() {
+               super();
+               // TODO Auto-generated constructor stub
+       }
+
+       public Goal(String goalname, String description, Set<Training> trainings) {
+               super();
+               this.goalname = goalname;
+               this.description = description;
+               this.trainings = trainings;
+       }
+
+       /**
+        * @return the id
+        */
+       public Long getId() {
+               return id;
+       }
+
+       /**
+        * @param id the id to set
+        */
+       public void setId(Long id) {
+               this.id = id;
+       }
+
+       /**
+        * @return the goalname
+        */
+       public String getGoalname() {
+               return goalname;
+       }
+
+       /**
+        * @param goalname the goalname to set
+        */
+       public void setGoalname(String goalname) {
+               this.goalname = goalname;
+       }
+
+       /**
+        * @return the description
+        */
+       public String getDescription() {
+               return description;
+       }
+
+       /**
+        * @param description the description to set
+        */
+       public void setDescription(String description) {
+               this.description = description;
+       }
+
+       /**
+        * @return the trainings
+        */
+       public Set<Training> getTrainings() {
+               return trainings;
+       }
+
+       /**
+        * @param trainings the trainings to set
+        */
+       public void setTrainings(Set<Training> trainings) {
+               this.trainings = trainings;
+       }
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((description == null) ? 0 : description.hashCode());
+               result = prime * result + ((goalname == null) ? 0 : goalname.hashCode());
+               result = prime * result + ((id == null) ? 0 : id.hashCode());
+               result = prime * result + ((trainings == null) ? 0 : trainings.hashCode());
+               return result;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               Goal other = (Goal) obj;
+               if (description == null) {
+                       if (other.description != null)
+                               return false;
+               } else if (!description.equals(other.description))
+                       return false;
+               if (goalname == null) {
+                       if (other.goalname != null)
+                               return false;
+               } else if (!goalname.equals(other.goalname))
+                       return false;
+               if (id == null) {
+                       if (other.id != null)
+                               return false;
+               } else if (!id.equals(other.id))
+                       return false;
+               if (trainings == null) {
+                       if (other.trainings != null)
+                               return false;
+               } else if (!trainings.equals(other.trainings))
+                       return false;
+               return true;
+       }
+
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("Goal [id=");
+               builder.append(id);
+               builder.append(", goalname=");
+               builder.append(goalname);
+               builder.append(", description=");
+               builder.append(description);
+               builder.append(", trainings=");
+               builder.append(trainings);
+               builder.append("]");
+               return builder.toString();
+       }
+    
+    
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Gym.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Gym.java
new file mode 100644 (file)
index 0000000..c61d298
--- /dev/null
@@ -0,0 +1,217 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinTable;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToMany;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "gym", schema = "athletik")
+public class Gym {
+    
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    
+    @Column(nullable = false)
+    private String gymname;
+    
+    @Column
+    private String gymlocation;
+    
+    @ManyToMany(mappedBy = "gyms")
+    private Set<Athlet> athleten = new HashSet<>();
+    
+    @ManyToMany
+    @JoinTable(
+        name = "gymtool", 
+        schema = "athletik",
+        joinColumns = @JoinColumn(name = "fk_gym"), 
+        inverseJoinColumns = @JoinColumn(name = "fk_tool")
+    )
+    private Set<Tool> tools = new HashSet<>();
+
+       public Gym() {
+               super();
+               // TODO Auto-generated constructor stub
+       }
+
+       public Gym(String gymname, String gymlocation, Set<Athlet> athleten, Set<Tool> tools) {
+               super();
+               this.gymname = gymname;
+               this.gymlocation = gymlocation;
+               this.athleten = athleten;
+               this.tools = tools;
+       }
+
+       /**
+        * @return the id
+        */
+       public Long getId() {
+               return id;
+       }
+
+       /**
+        * @param id the id to set
+        */
+       public void setId(Long id) {
+               this.id = id;
+       }
+
+       /**
+        * @return the gymname
+        */
+       public String getGymname() {
+               return gymname;
+       }
+
+       /**
+        * @param gymname the gymname to set
+        */
+       public void setGymname(String gymname) {
+               this.gymname = gymname;
+       }
+
+       /**
+        * @return the gymlocation
+        */
+       public String getGymlocation() {
+               return gymlocation;
+       }
+
+       /**
+        * @param gymlocation the gymlocation to set
+        */
+       public void setGymlocation(String gymlocation) {
+               this.gymlocation = gymlocation;
+       }
+
+       /**
+        * @return the athleten
+        */
+       public Set<Athlet> getAthleten() {
+               return athleten;
+       }
+
+       /**
+        * @param athleten the athleten to set
+        */
+       public void setAthleten(Set<Athlet> athleten) {
+               this.athleten = athleten;
+       }
+
+       /**
+        * @return the tools
+        */
+       public Set<Tool> getTools() {
+               return tools;
+       }
+
+       /**
+        * @param tools the tools to set
+        */
+       public void setTools(Set<Tool> tools) {
+               this.tools = tools;
+       }
+       
+       // Hilfsmethoden
+/*
+    public void addAthlet(Athlet athlet) {
+        this.athleten.add(athlet);
+        athlet.getGyms().add(this);
+    }
+    
+    public void removeAthlet(Athlet athlet) {
+        this.athleten.remove(athlet);
+        athlet.getGyms().remove(this);
+    }
+    
+    public void addTool(Tool tool) {
+        this.tools.add(tool);
+        tool.getGyms().add(this);
+    }
+    
+    public void removeTool(Tool tool) {
+        this.tools.remove(tool);
+        tool.getGyms().remove(this);
+    }
+*/
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((athleten == null) ? 0 : athleten.hashCode());
+               result = prime * result + ((gymlocation == null) ? 0 : gymlocation.hashCode());
+               result = prime * result + ((gymname == null) ? 0 : gymname.hashCode());
+               result = prime * result + ((id == null) ? 0 : id.hashCode());
+               result = prime * result + ((tools == null) ? 0 : tools.hashCode());
+               return result;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               Gym other = (Gym) obj;
+               if (athleten == null) {
+                       if (other.athleten != null)
+                               return false;
+               } else if (!athleten.equals(other.athleten))
+                       return false;
+               if (gymlocation == null) {
+                       if (other.gymlocation != null)
+                               return false;
+               } else if (!gymlocation.equals(other.gymlocation))
+                       return false;
+               if (gymname == null) {
+                       if (other.gymname != null)
+                               return false;
+               } else if (!gymname.equals(other.gymname))
+                       return false;
+               if (id == null) {
+                       if (other.id != null)
+                               return false;
+               } else if (!id.equals(other.id))
+                       return false;
+               if (tools == null) {
+                       if (other.tools != null)
+                               return false;
+               } else if (!tools.equals(other.tools))
+                       return false;
+               return true;
+       }
+
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("Gym [id=");
+               builder.append(id);
+               builder.append(", gymname=");
+               builder.append(gymname);
+               builder.append(", gymlocation=");
+               builder.append(gymlocation);
+               builder.append(", athleten=");
+               builder.append(athleten);
+               builder.append(", tools=");
+               builder.append(tools);
+               builder.append("]");
+               return builder.toString();
+       }
+    
+
+    
+    
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Log.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Log.java
new file mode 100644 (file)
index 0000000..bdf6c2e
--- /dev/null
@@ -0,0 +1,328 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.PrePersist;
+import javax.persistence.Table;
+import java.time.LocalDateTime;
+
+@Entity
+@Table(name = "log", schema = "athletik")
+public class Log {
+    
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    
+    @Column(name = "created", nullable = false, updatable = false)
+    private LocalDateTime created;
+    
+    @Column(name = "logdata", nullable = false, columnDefinition = "TEXT")
+    private String logdata;
+    
+    @Column(name = "sid", nullable = false, length = 100)
+    private String sid;
+    
+    @Column(name = "script", nullable = false, length = 255)
+    private String script;
+    
+    @Column(name = "log_level", length = 20)
+    private String logLevel;
+    
+    @Column(name = "ip_address", length = 45)
+    private String ipAddress;
+    
+    @Column(name = "user_agent", length = 500)
+    private String userAgent;
+    
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "fk_athlet", nullable = false)
+    private Athlet athlet;
+    
+    // Konstruktoren
+    public Log() {
+        this.created = LocalDateTime.now();
+    }
+    
+    
+    
+    public Log(LocalDateTime created, String logdata, String sid, String script, String logLevel, String ipAddress,
+                       String userAgent, Athlet athlet) {
+               super();
+               this.created = created;
+               this.logdata = logdata;
+               this.sid = sid;
+               this.script = script;
+               this.logLevel = logLevel;
+               this.ipAddress = ipAddress;
+               this.userAgent = userAgent;
+               this.athlet = athlet;
+       }
+
+
+
+       public Log(String logdata, String sid, String script, Athlet athlet) {
+        this();
+        this.logdata = logdata;
+        this.sid = sid;
+        this.script = script;
+        this.athlet = athlet;
+    }
+    
+    public Log(String logdata, String sid, String script, String logLevel, Athlet athlet) {
+        this(logdata, sid, script, athlet);
+        this.logLevel = logLevel;
+    }
+    
+    public Log(String logdata, String sid, String script, String logLevel, String ipAddress, String userAgent, Athlet athlet) {
+        this(logdata, sid, script, logLevel, athlet);
+        this.ipAddress = ipAddress;
+        this.userAgent = userAgent;
+    }
+    
+    // Pre-Persist Callback
+    @PrePersist
+    protected void onCreate() {
+        if (created == null) {
+            created = LocalDateTime.now();
+        }
+    }
+
+       /**
+        * @return the id
+        */
+       public Long getId() {
+               return id;
+       }
+
+       /**
+        * @param id the id to set
+        */
+       public void setId(Long id) {
+               this.id = id;
+       }
+
+       /**
+        * @return the created
+        */
+       public LocalDateTime getCreated() {
+               return created;
+       }
+
+       /**
+        * @param created the created to set
+        */
+       public void setCreated(LocalDateTime created) {
+               this.created = created;
+       }
+
+       /**
+        * @return the logdata
+        */
+       public String getLogdata() {
+               return logdata;
+       }
+
+       /**
+        * @param logdata the logdata to set
+        */
+       public void setLogdata(String logdata) {
+               this.logdata = logdata;
+       }
+
+       /**
+        * @return the sid
+        */
+       public String getSid() {
+               return sid;
+       }
+
+       /**
+        * @param sid the sid to set
+        */
+       public void setSid(String sid) {
+               this.sid = sid;
+       }
+
+       /**
+        * @return the script
+        */
+       public String getScript() {
+               return script;
+       }
+
+       /**
+        * @param script the script to set
+        */
+       public void setScript(String script) {
+               this.script = script;
+       }
+
+       /**
+        * @return the logLevel
+        */
+       public String getLogLevel() {
+               return logLevel;
+       }
+
+       /**
+        * @param logLevel the logLevel to set
+        */
+       public void setLogLevel(String logLevel) {
+               this.logLevel = logLevel;
+       }
+
+       /**
+        * @return the ipAddress
+        */
+       public String getIpAddress() {
+               return ipAddress;
+       }
+
+       /**
+        * @param ipAddress the ipAddress to set
+        */
+       public void setIpAddress(String ipAddress) {
+               this.ipAddress = ipAddress;
+       }
+
+       /**
+        * @return the userAgent
+        */
+       public String getUserAgent() {
+               return userAgent;
+       }
+
+       /**
+        * @param userAgent the userAgent to set
+        */
+       public void setUserAgent(String userAgent) {
+               this.userAgent = userAgent;
+       }
+
+       /**
+        * @return the athlet
+        */
+       public Athlet getAthlet() {
+               return athlet;
+       }
+
+       /**
+        * @param athlet the athlet to set
+        */
+       public void setAthlet(Athlet athlet) {
+               this.athlet = athlet;
+       }
+
+
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((athlet == null) ? 0 : athlet.hashCode());
+               result = prime * result + ((created == null) ? 0 : created.hashCode());
+               result = prime * result + ((id == null) ? 0 : id.hashCode());
+               result = prime * result + ((ipAddress == null) ? 0 : ipAddress.hashCode());
+               result = prime * result + ((logLevel == null) ? 0 : logLevel.hashCode());
+               result = prime * result + ((logdata == null) ? 0 : logdata.hashCode());
+               result = prime * result + ((script == null) ? 0 : script.hashCode());
+               result = prime * result + ((sid == null) ? 0 : sid.hashCode());
+               result = prime * result + ((userAgent == null) ? 0 : userAgent.hashCode());
+               return result;
+       }
+
+
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               Log other = (Log) obj;
+               if (athlet == null) {
+                       if (other.athlet != null)
+                               return false;
+               } else if (!athlet.equals(other.athlet))
+                       return false;
+               if (created == null) {
+                       if (other.created != null)
+                               return false;
+               } else if (!created.equals(other.created))
+                       return false;
+               if (id == null) {
+                       if (other.id != null)
+                               return false;
+               } else if (!id.equals(other.id))
+                       return false;
+               if (ipAddress == null) {
+                       if (other.ipAddress != null)
+                               return false;
+               } else if (!ipAddress.equals(other.ipAddress))
+                       return false;
+               if (logLevel == null) {
+                       if (other.logLevel != null)
+                               return false;
+               } else if (!logLevel.equals(other.logLevel))
+                       return false;
+               if (logdata == null) {
+                       if (other.logdata != null)
+                               return false;
+               } else if (!logdata.equals(other.logdata))
+                       return false;
+               if (script == null) {
+                       if (other.script != null)
+                               return false;
+               } else if (!script.equals(other.script))
+                       return false;
+               if (sid == null) {
+                       if (other.sid != null)
+                               return false;
+               } else if (!sid.equals(other.sid))
+                       return false;
+               if (userAgent == null) {
+                       if (other.userAgent != null)
+                               return false;
+               } else if (!userAgent.equals(other.userAgent))
+                       return false;
+               return true;
+       }
+
+
+
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("Log [id=");
+               builder.append(id);
+               builder.append(", created=");
+               builder.append(created);
+               builder.append(", logdata=");
+               builder.append(logdata);
+               builder.append(", sid=");
+               builder.append(sid);
+               builder.append(", script=");
+               builder.append(script);
+               builder.append(", logLevel=");
+               builder.append(logLevel);
+               builder.append(", ipAddress=");
+               builder.append(ipAddress);
+               builder.append(", userAgent=");
+               builder.append(userAgent);
+               builder.append(", athlet=");
+               builder.append(athlet);
+               builder.append("]");
+               return builder.toString();
+       }    
+    
+    
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Muscle.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Muscle.java
new file mode 100644 (file)
index 0000000..ea215d7
--- /dev/null
@@ -0,0 +1,289 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "muscle", schema = "athletik")
+public class Muscle {
+
+       @Id
+       @GeneratedValue(strategy = GenerationType.IDENTITY)
+       private Long id;
+
+       @Column(name = "muscle_name_latin", nullable = false)
+       private String muscleNameLatin;
+
+       @Column(name = "muscle_name_english")
+       private String muscleNameEnglish;
+
+       @Column(name = "muscle_name_german")
+       private String muscleNameGerman;
+
+       @Column(name = "movement_english", nullable = false)
+       private String movementEnglish;
+
+       @Column(name = "movement_german", nullable = false)
+       private String movementGerman;
+
+       @Column(name = "musclegroup_name_english")
+       private String musclegroupNameEnglish;
+
+       @Column(name = "musclegroup_name_german")
+       private String musclegroupNameGerman;
+
+       @ManyToMany(mappedBy = "muscles")
+       private Set<Exercise> exercises = new HashSet<>();
+
+       public Muscle() {
+               super();
+               // TODO Auto-generated constructor stub
+       }
+
+       public Muscle(String muscleNameLatin, String muscleNameEnglish, String muscleNameGerman, String movementEnglish,
+                       String movementGerman, String musclegroupNameEnglish, String musclegroupNameGerman,
+                       Set<Exercise> exercises) {
+               super();
+               this.muscleNameLatin = muscleNameLatin;
+               this.muscleNameEnglish = muscleNameEnglish;
+               this.muscleNameGerman = muscleNameGerman;
+               this.movementEnglish = movementEnglish;
+               this.movementGerman = movementGerman;
+               this.musclegroupNameEnglish = musclegroupNameEnglish;
+               this.musclegroupNameGerman = musclegroupNameGerman;
+               this.exercises = exercises;
+       }
+
+       /**
+        * @return the id
+        */
+       public Long getId() {
+               return id;
+       }
+
+       /**
+        * @param id the id to set
+        */
+       public void setId(Long id) {
+               this.id = id;
+       }
+
+       /**
+        * @return the muscleNameLatin
+        */
+       public String getMuscleNameLatin() {
+               return muscleNameLatin;
+       }
+
+       /**
+        * @param muscleNameLatin the muscleNameLatin to set
+        */
+       public void setMuscleNameLatin(String muscleNameLatin) {
+               this.muscleNameLatin = muscleNameLatin;
+       }
+
+       /**
+        * @return the muscleNameEnglish
+        */
+       public String getMuscleNameEnglish() {
+               return muscleNameEnglish;
+       }
+
+       /**
+        * @param muscleNameEnglish the muscleNameEnglish to set
+        */
+       public void setMuscleNameEnglish(String muscleNameEnglish) {
+               this.muscleNameEnglish = muscleNameEnglish;
+       }
+
+       /**
+        * @return the muscleNameGerman
+        */
+       public String getMuscleNameGerman() {
+               return muscleNameGerman;
+       }
+
+       /**
+        * @param muscleNameGerman the muscleNameGerman to set
+        */
+       public void setMuscleNameGerman(String muscleNameGerman) {
+               this.muscleNameGerman = muscleNameGerman;
+       }
+
+       /**
+        * @return the movementEnglish
+        */
+       public String getMovementEnglish() {
+               return movementEnglish;
+       }
+
+       /**
+        * @param movementEnglish the movementEnglish to set
+        */
+       public void setMovementEnglish(String movementEnglish) {
+               this.movementEnglish = movementEnglish;
+       }
+
+       /**
+        * @return the movementGerman
+        */
+       public String getMovementGerman() {
+               return movementGerman;
+       }
+
+       /**
+        * @param movementGerman the movementGerman to set
+        */
+       public void setMovementGerman(String movementGerman) {
+               this.movementGerman = movementGerman;
+       }
+
+       /**
+        * @return the musclegroupNameEnglish
+        */
+       public String getMusclegroupNameEnglish() {
+               return musclegroupNameEnglish;
+       }
+
+       /**
+        * @param musclegroupNameEnglish the musclegroupNameEnglish to set
+        */
+       public void setMusclegroupNameEnglish(String musclegroupNameEnglish) {
+               this.musclegroupNameEnglish = musclegroupNameEnglish;
+       }
+
+       /**
+        * @return the musclegroupNameGerman
+        */
+       public String getMusclegroupNameGerman() {
+               return musclegroupNameGerman;
+       }
+
+       /**
+        * @param musclegroupNameGerman the musclegroupNameGerman to set
+        */
+       public void setMusclegroupNameGerman(String musclegroupNameGerman) {
+               this.musclegroupNameGerman = musclegroupNameGerman;
+       }
+
+       /**
+        * @return the exercises
+        */
+       public Set<Exercise> getExercises() {
+               return exercises;
+       }
+
+       /**
+        * @param exercises the exercises to set
+        */
+       public void setExercises(Set<Exercise> exercises) {
+               this.exercises = exercises;
+       }
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((exercises == null) ? 0 : exercises.hashCode());
+               result = prime * result + ((id == null) ? 0 : id.hashCode());
+               result = prime * result + ((movementEnglish == null) ? 0 : movementEnglish.hashCode());
+               result = prime * result + ((movementGerman == null) ? 0 : movementGerman.hashCode());
+               result = prime * result + ((muscleNameEnglish == null) ? 0 : muscleNameEnglish.hashCode());
+               result = prime * result + ((muscleNameGerman == null) ? 0 : muscleNameGerman.hashCode());
+               result = prime * result + ((muscleNameLatin == null) ? 0 : muscleNameLatin.hashCode());
+               result = prime * result + ((musclegroupNameEnglish == null) ? 0 : musclegroupNameEnglish.hashCode());
+               result = prime * result + ((musclegroupNameGerman == null) ? 0 : musclegroupNameGerman.hashCode());
+               return result;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               Muscle other = (Muscle) obj;
+               if (exercises == null) {
+                       if (other.exercises != null)
+                               return false;
+               } else if (!exercises.equals(other.exercises))
+                       return false;
+               if (id == null) {
+                       if (other.id != null)
+                               return false;
+               } else if (!id.equals(other.id))
+                       return false;
+               if (movementEnglish == null) {
+                       if (other.movementEnglish != null)
+                               return false;
+               } else if (!movementEnglish.equals(other.movementEnglish))
+                       return false;
+               if (movementGerman == null) {
+                       if (other.movementGerman != null)
+                               return false;
+               } else if (!movementGerman.equals(other.movementGerman))
+                       return false;
+               if (muscleNameEnglish == null) {
+                       if (other.muscleNameEnglish != null)
+                               return false;
+               } else if (!muscleNameEnglish.equals(other.muscleNameEnglish))
+                       return false;
+               if (muscleNameGerman == null) {
+                       if (other.muscleNameGerman != null)
+                               return false;
+               } else if (!muscleNameGerman.equals(other.muscleNameGerman))
+                       return false;
+               if (muscleNameLatin == null) {
+                       if (other.muscleNameLatin != null)
+                               return false;
+               } else if (!muscleNameLatin.equals(other.muscleNameLatin))
+                       return false;
+               if (musclegroupNameEnglish == null) {
+                       if (other.musclegroupNameEnglish != null)
+                               return false;
+               } else if (!musclegroupNameEnglish.equals(other.musclegroupNameEnglish))
+                       return false;
+               if (musclegroupNameGerman == null) {
+                       if (other.musclegroupNameGerman != null)
+                               return false;
+               } else if (!musclegroupNameGerman.equals(other.musclegroupNameGerman))
+                       return false;
+               return true;
+       }
+
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("Muscle [id=");
+               builder.append(id);
+               builder.append(", muscleNameLatin=");
+               builder.append(muscleNameLatin);
+               builder.append(", muscleNameEnglish=");
+               builder.append(muscleNameEnglish);
+               builder.append(", muscleNameGerman=");
+               builder.append(muscleNameGerman);
+               builder.append(", movementEnglish=");
+               builder.append(movementEnglish);
+               builder.append(", movementGerman=");
+               builder.append(movementGerman);
+               builder.append(", musclegroupNameEnglish=");
+               builder.append(musclegroupNameEnglish);
+               builder.append(", musclegroupNameGerman=");
+               builder.append(musclegroupNameGerman);
+               builder.append(", exercises=");
+               builder.append(exercises);
+               builder.append("]");
+               return builder.toString();
+       }
+
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Protokoll.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Protokoll.java
new file mode 100644 (file)
index 0000000..969e985
--- /dev/null
@@ -0,0 +1,398 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.time.LocalDateTime;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "protokoll", schema = "athletik")
+public class Protokoll {
+    
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    
+    @Column(name = "trainingdate", nullable = false)
+    private LocalDateTime trainingDate;
+    
+    @Column(name = "number_of_set", nullable = false)
+    private Integer numberOfSet;
+    
+    @Column(name = "repetitions", nullable = false)
+    private Integer repetitions;
+    
+    @Column(name = "weight", precision = 5, scale = 2)
+    private Double weight;
+    
+    @Column(name = "is_test", nullable = false)
+    private Boolean isTest = false;
+    
+    @Column(name = "volume")
+    private Double volume;
+    
+    @Column(name = "one_rep_max")
+    private Double oneRepMax;
+    
+    @Column(name = "notes", length = 500)
+    private String notes;
+    
+    @Column(name = "rpe") // Rate of Perceived Exertion
+    private Integer rpe;
+    
+    @Column(name = "rest_time") // in seconds
+    private Integer restTime;
+    
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "fk_athlet", nullable = false)
+    private Athlet athlet;
+    
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "fk_exercise", nullable = false)
+    private Exercise exercise;
+
+       public Protokoll() {
+               super();
+               // TODO Auto-generated constructor stub
+       }
+
+       public Protokoll(LocalDateTime trainingDate, Integer numberOfSet, Integer repetitions, Double weight,
+                       Boolean isTest, Double volume, Double oneRepMax, String notes, Integer rpe, Integer restTime, Athlet athlet,
+                       Exercise exercise) {
+               super();
+               this.trainingDate = trainingDate;
+               this.numberOfSet = numberOfSet;
+               this.repetitions = repetitions;
+               this.weight = weight;
+               this.isTest = isTest;
+               this.volume = volume;
+               this.oneRepMax = oneRepMax;
+               this.notes = notes;
+               this.rpe = rpe;
+               this.restTime = restTime;
+               this.athlet = athlet;
+               this.exercise = exercise;
+       }
+
+       /**
+        * @return the id
+        */
+       public Long getId() {
+               return id;
+       }
+
+       /**
+        * @param id the id to set
+        */
+       public void setId(Long id) {
+               this.id = id;
+       }
+
+       /**
+        * @return the trainingDate
+        */
+       public LocalDateTime getTrainingDate() {
+               return trainingDate;
+       }
+
+       /**
+        * @param trainingDate the trainingDate to set
+        */
+       public void setTrainingDate(LocalDateTime trainingDate) {
+               this.trainingDate = trainingDate;
+       }
+
+       /**
+        * @return the numberOfSet
+        */
+       public Integer getNumberOfSet() {
+               return numberOfSet;
+       }
+
+       /**
+        * @param numberOfSet the numberOfSet to set
+        */
+       public void setNumberOfSet(Integer numberOfSet) {
+               this.numberOfSet = numberOfSet;
+       }
+
+       /**
+        * @return the repetitions
+        */
+       public Integer getRepetitions() {
+               return repetitions;
+       }
+
+       /**
+        * @param repetitions the repetitions to set
+        */
+       public void setRepetitions(Integer repetitions) {
+               this.repetitions = repetitions;
+       }
+
+       /**
+        * @return the weight
+        */
+       public Double getWeight() {
+               return weight;
+       }
+
+       /**
+        * @param weight the weight to set
+        */
+       public void setWeight(Double weight) {
+               this.weight = weight;
+       }
+
+       /**
+        * @return the isTest
+        */
+       public Boolean getIsTest() {
+               return isTest;
+       }
+
+       /**
+        * @param isTest the isTest to set
+        */
+       public void setIsTest(Boolean isTest) {
+               this.isTest = isTest;
+       }
+
+       /**
+        * @return the volume
+        */
+       public Double getVolume() {
+               return volume;
+       }
+
+       /**
+        * @param volume the volume to set
+        */
+       public void setVolume(Double volume) {
+               this.volume = volume;
+       }
+
+       /**
+        * @return the oneRepMax
+        */
+       public Double getOneRepMax() {
+               return oneRepMax;
+       }
+
+       /**
+        * @param oneRepMax the oneRepMax to set
+        */
+       public void setOneRepMax(Double oneRepMax) {
+               this.oneRepMax = oneRepMax;
+       }
+
+       /**
+        * @return the notes
+        */
+       public String getNotes() {
+               return notes;
+       }
+
+       /**
+        * @param notes the notes to set
+        */
+       public void setNotes(String notes) {
+               this.notes = notes;
+       }
+
+       /**
+        * @return the rpe
+        */
+       public Integer getRpe() {
+               return rpe;
+       }
+
+       /**
+        * @param rpe the rpe to set
+        */
+       public void setRpe(Integer rpe) {
+               this.rpe = rpe;
+       }
+
+       /**
+        * @return the restTime
+        */
+       public Integer getRestTime() {
+               return restTime;
+       }
+
+       /**
+        * @param restTime the restTime to set
+        */
+       public void setRestTime(Integer restTime) {
+               this.restTime = restTime;
+       }
+
+       /**
+        * @return the athlet
+        */
+       public Athlet getAthlet() {
+               return athlet;
+       }
+
+       /**
+        * @param athlet the athlet to set
+        */
+       public void setAthlet(Athlet athlet) {
+               this.athlet = athlet;
+       }
+
+       /**
+        * @return the exercise
+        */
+       public Exercise getExercise() {
+               return exercise;
+       }
+
+       /**
+        * @param exercise the exercise to set
+        */
+       public void setExercise(Exercise exercise) {
+               this.exercise = exercise;
+       }
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((athlet == null) ? 0 : athlet.hashCode());
+               result = prime * result + ((exercise == null) ? 0 : exercise.hashCode());
+               result = prime * result + ((id == null) ? 0 : id.hashCode());
+               result = prime * result + ((isTest == null) ? 0 : isTest.hashCode());
+               result = prime * result + ((notes == null) ? 0 : notes.hashCode());
+               result = prime * result + ((numberOfSet == null) ? 0 : numberOfSet.hashCode());
+               result = prime * result + ((oneRepMax == null) ? 0 : oneRepMax.hashCode());
+               result = prime * result + ((repetitions == null) ? 0 : repetitions.hashCode());
+               result = prime * result + ((restTime == null) ? 0 : restTime.hashCode());
+               result = prime * result + ((rpe == null) ? 0 : rpe.hashCode());
+               result = prime * result + ((trainingDate == null) ? 0 : trainingDate.hashCode());
+               result = prime * result + ((volume == null) ? 0 : volume.hashCode());
+               result = prime * result + ((weight == null) ? 0 : weight.hashCode());
+               return result;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               Protokoll other = (Protokoll) obj;
+               if (athlet == null) {
+                       if (other.athlet != null)
+                               return false;
+               } else if (!athlet.equals(other.athlet))
+                       return false;
+               if (exercise == null) {
+                       if (other.exercise != null)
+                               return false;
+               } else if (!exercise.equals(other.exercise))
+                       return false;
+               if (id == null) {
+                       if (other.id != null)
+                               return false;
+               } else if (!id.equals(other.id))
+                       return false;
+               if (isTest == null) {
+                       if (other.isTest != null)
+                               return false;
+               } else if (!isTest.equals(other.isTest))
+                       return false;
+               if (notes == null) {
+                       if (other.notes != null)
+                               return false;
+               } else if (!notes.equals(other.notes))
+                       return false;
+               if (numberOfSet == null) {
+                       if (other.numberOfSet != null)
+                               return false;
+               } else if (!numberOfSet.equals(other.numberOfSet))
+                       return false;
+               if (oneRepMax == null) {
+                       if (other.oneRepMax != null)
+                               return false;
+               } else if (!oneRepMax.equals(other.oneRepMax))
+                       return false;
+               if (repetitions == null) {
+                       if (other.repetitions != null)
+                               return false;
+               } else if (!repetitions.equals(other.repetitions))
+                       return false;
+               if (restTime == null) {
+                       if (other.restTime != null)
+                               return false;
+               } else if (!restTime.equals(other.restTime))
+                       return false;
+               if (rpe == null) {
+                       if (other.rpe != null)
+                               return false;
+               } else if (!rpe.equals(other.rpe))
+                       return false;
+               if (trainingDate == null) {
+                       if (other.trainingDate != null)
+                               return false;
+               } else if (!trainingDate.equals(other.trainingDate))
+                       return false;
+               if (volume == null) {
+                       if (other.volume != null)
+                               return false;
+               } else if (!volume.equals(other.volume))
+                       return false;
+               if (weight == null) {
+                       if (other.weight != null)
+                               return false;
+               } else if (!weight.equals(other.weight))
+                       return false;
+               return true;
+       }
+
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("Protokoll [id=");
+               builder.append(id);
+               builder.append(", trainingDate=");
+               builder.append(trainingDate);
+               builder.append(", numberOfSet=");
+               builder.append(numberOfSet);
+               builder.append(", repetitions=");
+               builder.append(repetitions);
+               builder.append(", weight=");
+               builder.append(weight);
+               builder.append(", isTest=");
+               builder.append(isTest);
+               builder.append(", volume=");
+               builder.append(volume);
+               builder.append(", oneRepMax=");
+               builder.append(oneRepMax);
+               builder.append(", notes=");
+               builder.append(notes);
+               builder.append(", rpe=");
+               builder.append(rpe);
+               builder.append(", restTime=");
+               builder.append(restTime);
+               builder.append(", athlet=");
+               builder.append(athlet);
+               builder.append(", exercise=");
+               builder.append(exercise);
+               builder.append("]");
+               return builder.toString();
+       }
+    
+   
+}
+
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Tool.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Tool.java
new file mode 100644 (file)
index 0000000..7f4e2a6
--- /dev/null
@@ -0,0 +1,235 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.math.BigDecimal;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "tools", schema = "athletik")
+public class Tool {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    
+    @Column(name = "toolname_english", nullable = false)
+    private String toolnameEnglish;
+    
+    @Column(name = "toolname_german", nullable = false)
+    private String toolnameGerman;
+    
+    @Column(name = "min_weight", precision = 5, scale = 2)
+    private BigDecimal minWeight = BigDecimal.ZERO;
+    
+    @Column(name = "steps_weight", precision = 5, scale = 2)
+    private BigDecimal stepsWeight = BigDecimal.ZERO;
+    
+    @Column(name = "max_weight", precision = 5, scale = 2)
+    private BigDecimal maxWeight = BigDecimal.ZERO;
+    
+    @Column(name = "description")
+    private String description;
+    
+       public Tool() {
+               super();
+               // TODO Auto-generated constructor stub
+       }
+
+       public Tool(String toolnameEnglish, String toolnameGerman, BigDecimal minWeight, BigDecimal stepsWeight,
+                       BigDecimal maxWeight, String description) {
+               super();
+               this.toolnameEnglish = toolnameEnglish;
+               this.toolnameGerman = toolnameGerman;
+               this.minWeight = minWeight;
+               this.stepsWeight = stepsWeight;
+               this.maxWeight = maxWeight;
+               this.description = description;
+       }
+
+       /**
+        * @return the id
+        */
+       public Long getId() {
+               return id;
+       }
+
+       /**
+        * @param id the id to set
+        */
+       public void setId(Long id) {
+               this.id = id;
+       }
+
+       /**
+        * @return the toolnameEnglish
+        */
+       public String getToolnameEnglish() {
+               return toolnameEnglish;
+       }
+
+       /**
+        * @param toolnameEnglish the toolnameEnglish to set
+        */
+       public void setToolnameEnglish(String toolnameEnglish) {
+               this.toolnameEnglish = toolnameEnglish;
+       }
+
+       /**
+        * @return the toolnameGerman
+        */
+       public String getToolnameGerman() {
+               return toolnameGerman;
+       }
+
+       /**
+        * @param toolnameGerman the toolnameGerman to set
+        */
+       public void setToolnameGerman(String toolnameGerman) {
+               this.toolnameGerman = toolnameGerman;
+       }
+
+       /**
+        * @return the minWeight
+        */
+       public BigDecimal getMinWeight() {
+               return minWeight;
+       }
+
+       /**
+        * @param minWeight the minWeight to set
+        */
+       public void setMinWeight(BigDecimal minWeight) {
+               this.minWeight = minWeight;
+       }
+
+       /**
+        * @return the stepsWeight
+        */
+       public BigDecimal getStepsWeight() {
+               return stepsWeight;
+       }
+
+       /**
+        * @param stepsWeight the stepsWeight to set
+        */
+       public void setStepsWeight(BigDecimal stepsWeight) {
+               this.stepsWeight = stepsWeight;
+       }
+
+       /**
+        * @return the maxWeight
+        */
+       public BigDecimal getMaxWeight() {
+               return maxWeight;
+       }
+
+       /**
+        * @param maxWeight the maxWeight to set
+        */
+       public void setMaxWeight(BigDecimal maxWeight) {
+               this.maxWeight = maxWeight;
+       }
+
+       /**
+        * @return the description
+        */
+       public String getDescription() {
+               return description;
+       }
+
+       /**
+        * @param description the description to set
+        */
+       public void setDescription(String description) {
+               this.description = description;
+       }
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((description == null) ? 0 : description.hashCode());
+               result = prime * result + ((id == null) ? 0 : id.hashCode());
+               result = prime * result + ((maxWeight == null) ? 0 : maxWeight.hashCode());
+               result = prime * result + ((minWeight == null) ? 0 : minWeight.hashCode());
+               result = prime * result + ((stepsWeight == null) ? 0 : stepsWeight.hashCode());
+               result = prime * result + ((toolnameEnglish == null) ? 0 : toolnameEnglish.hashCode());
+               result = prime * result + ((toolnameGerman == null) ? 0 : toolnameGerman.hashCode());
+               return result;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               Tool other = (Tool) obj;
+               if (description == null) {
+                       if (other.description != null)
+                               return false;
+               } else if (!description.equals(other.description))
+                       return false;
+               if (id == null) {
+                       if (other.id != null)
+                               return false;
+               } else if (!id.equals(other.id))
+                       return false;
+               if (maxWeight == null) {
+                       if (other.maxWeight != null)
+                               return false;
+               } else if (!maxWeight.equals(other.maxWeight))
+                       return false;
+               if (minWeight == null) {
+                       if (other.minWeight != null)
+                               return false;
+               } else if (!minWeight.equals(other.minWeight))
+                       return false;
+               if (stepsWeight == null) {
+                       if (other.stepsWeight != null)
+                               return false;
+               } else if (!stepsWeight.equals(other.stepsWeight))
+                       return false;
+               if (toolnameEnglish == null) {
+                       if (other.toolnameEnglish != null)
+                               return false;
+               } else if (!toolnameEnglish.equals(other.toolnameEnglish))
+                       return false;
+               if (toolnameGerman == null) {
+                       if (other.toolnameGerman != null)
+                               return false;
+               } else if (!toolnameGerman.equals(other.toolnameGerman))
+                       return false;
+               return true;
+       }
+
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("Tools [id=");
+               builder.append(id);
+               builder.append(", toolnameEnglish=");
+               builder.append(toolnameEnglish);
+               builder.append(", toolnameGerman=");
+               builder.append(toolnameGerman);
+               builder.append(", minWeight=");
+               builder.append(minWeight);
+               builder.append(", stepsWeight=");
+               builder.append(stepsWeight);
+               builder.append(", maxWeight=");
+               builder.append(maxWeight);
+               builder.append(", description=");
+               builder.append(description);
+               builder.append("]");
+               return builder.toString();
+       }
+
+   
+    
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Training.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/Training.java
new file mode 100644 (file)
index 0000000..f6853a6
--- /dev/null
@@ -0,0 +1,442 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.time.LocalDateTime;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.PrePersist;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "training", schema = "athletik")
+public class Training {
+    
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    
+    @Column(name = "title", nullable = false, length = 200)
+    private String title;
+    
+    @Column(name = "description", columnDefinition = "TEXT")
+    private String description;
+    
+    @Column(name = "repetitions_of_set")
+    private Integer repetitionsOfSet;
+    
+    @Column(name = "repetitions")
+    private Integer repetitions;
+    
+    @Column(name = "factor_of_fmax", precision = 5, scale = 2)
+    private Double factorOfFmax;
+    
+    @Column(name = "created", nullable = false, updatable = false)
+    private LocalDateTime created;
+    
+    @Column(name = "valid_since", nullable = false)
+    private LocalDateTime validSince;
+    
+    @Column(name = "valid_until")
+    private LocalDateTime validUntil;
+    
+    @Column(name = "is_active")
+    private Boolean isActive = true;
+    
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "fk_goal", nullable = false)
+    private Goal goal;
+    
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "fk_athlet", nullable = false)
+    private Athlet athlet;
+    
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "fk_exercise", nullable = false)
+    private Exercise exercise;
+    
+       public Training() {
+               super();
+               // TODO Auto-generated constructor stub
+       }
+   
+    
+    public Training(String title, String description, Integer repetitionsOfSet, Integer repetitions,
+                       Double factorOfFmax, LocalDateTime created, LocalDateTime validSince, LocalDateTime validUntil,
+                       Boolean isActive, Goal goal, Athlet athlet, Exercise exercise) {
+               super();
+               this.title = title;
+               this.description = description;
+               this.repetitionsOfSet = repetitionsOfSet;
+               this.repetitions = repetitions;
+               this.factorOfFmax = factorOfFmax;
+               this.created = created;
+               this.validSince = validSince;
+               this.validUntil = validUntil;
+               this.isActive = isActive;
+               this.goal = goal;
+               this.athlet = athlet;
+               this.exercise = exercise;
+       }
+
+
+
+       // Pre-Persist Callback
+    @PrePersist
+    protected void onCreate() {
+        if (created == null) {
+            created = LocalDateTime.now();
+        }
+        if (validSince == null) {
+            validSince = LocalDateTime.now();
+        }
+    }
+
+
+       /**
+        * @return the id
+        */
+       public Long getId() {
+               return id;
+       }
+
+
+       /**
+        * @param id the id to set
+        */
+       public void setId(Long id) {
+               this.id = id;
+       }
+
+
+       /**
+        * @return the title
+        */
+       public String getTitle() {
+               return title;
+       }
+
+
+       /**
+        * @param title the title to set
+        */
+       public void setTitle(String title) {
+               this.title = title;
+       }
+
+
+       /**
+        * @return the description
+        */
+       public String getDescription() {
+               return description;
+       }
+
+
+       /**
+        * @param description the description to set
+        */
+       public void setDescription(String description) {
+               this.description = description;
+       }
+
+
+       /**
+        * @return the repetitionsOfSet
+        */
+       public Integer getRepetitionsOfSet() {
+               return repetitionsOfSet;
+       }
+
+
+       /**
+        * @param repetitionsOfSet the repetitionsOfSet to set
+        */
+       public void setRepetitionsOfSet(Integer repetitionsOfSet) {
+               this.repetitionsOfSet = repetitionsOfSet;
+       }
+
+
+       /**
+        * @return the repetitions
+        */
+       public Integer getRepetitions() {
+               return repetitions;
+       }
+
+
+       /**
+        * @param repetitions the repetitions to set
+        */
+       public void setRepetitions(Integer repetitions) {
+               this.repetitions = repetitions;
+       }
+
+
+       /**
+        * @return the factorOfFmax
+        */
+       public Double getFactorOfFmax() {
+               return factorOfFmax;
+       }
+
+
+       /**
+        * @param factorOfFmax the factorOfFmax to set
+        */
+       public void setFactorOfFmax(Double factorOfFmax) {
+               this.factorOfFmax = factorOfFmax;
+       }
+
+
+       /**
+        * @return the created
+        */
+       public LocalDateTime getCreated() {
+               return created;
+       }
+
+
+       /**
+        * @param created the created to set
+        */
+       public void setCreated(LocalDateTime created) {
+               this.created = created;
+       }
+
+
+       /**
+        * @return the validSince
+        */
+       public LocalDateTime getValidSince() {
+               return validSince;
+       }
+
+
+       /**
+        * @param validSince the validSince to set
+        */
+       public void setValidSince(LocalDateTime validSince) {
+               this.validSince = validSince;
+       }
+
+
+       /**
+        * @return the validUntil
+        */
+       public LocalDateTime getValidUntil() {
+               return validUntil;
+       }
+
+
+       /**
+        * @param validUntil the validUntil to set
+        */
+       public void setValidUntil(LocalDateTime validUntil) {
+               this.validUntil = validUntil;
+       }
+
+
+       /**
+        * @return the isActive
+        */
+       public Boolean getIsActive() {
+               return isActive;
+       }
+
+
+       /**
+        * @param isActive the isActive to set
+        */
+       public void setIsActive(Boolean isActive) {
+               this.isActive = isActive;
+       }
+
+
+       /**
+        * @return the goal
+        */
+       public Goal getGoal() {
+               return goal;
+       }
+
+
+       /**
+        * @param goal the goal to set
+        */
+       public void setGoal(Goal goal) {
+               this.goal = goal;
+       }
+
+
+       /**
+        * @return the athlet
+        */
+       public Athlet getAthlet() {
+               return athlet;
+       }
+
+
+       /**
+        * @param athlet the athlet to set
+        */
+       public void setAthlet(Athlet athlet) {
+               this.athlet = athlet;
+       }
+
+
+       /**
+        * @return the exercise
+        */
+       public Exercise getExercise() {
+               return exercise;
+       }
+
+
+       /**
+        * @param exercise the exercise to set
+        */
+       public void setExercise(Exercise exercise) {
+               this.exercise = exercise;
+       }
+
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((athlet == null) ? 0 : athlet.hashCode());
+               result = prime * result + ((created == null) ? 0 : created.hashCode());
+               result = prime * result + ((description == null) ? 0 : description.hashCode());
+               result = prime * result + ((exercise == null) ? 0 : exercise.hashCode());
+               result = prime * result + ((factorOfFmax == null) ? 0 : factorOfFmax.hashCode());
+               result = prime * result + ((goal == null) ? 0 : goal.hashCode());
+               result = prime * result + ((id == null) ? 0 : id.hashCode());
+               result = prime * result + ((isActive == null) ? 0 : isActive.hashCode());
+               result = prime * result + ((repetitions == null) ? 0 : repetitions.hashCode());
+               result = prime * result + ((repetitionsOfSet == null) ? 0 : repetitionsOfSet.hashCode());
+               result = prime * result + ((title == null) ? 0 : title.hashCode());
+               result = prime * result + ((validSince == null) ? 0 : validSince.hashCode());
+               result = prime * result + ((validUntil == null) ? 0 : validUntil.hashCode());
+               return result;
+       }
+
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               Training other = (Training) obj;
+               if (athlet == null) {
+                       if (other.athlet != null)
+                               return false;
+               } else if (!athlet.equals(other.athlet))
+                       return false;
+               if (created == null) {
+                       if (other.created != null)
+                               return false;
+               } else if (!created.equals(other.created))
+                       return false;
+               if (description == null) {
+                       if (other.description != null)
+                               return false;
+               } else if (!description.equals(other.description))
+                       return false;
+               if (exercise == null) {
+                       if (other.exercise != null)
+                               return false;
+               } else if (!exercise.equals(other.exercise))
+                       return false;
+               if (factorOfFmax == null) {
+                       if (other.factorOfFmax != null)
+                               return false;
+               } else if (!factorOfFmax.equals(other.factorOfFmax))
+                       return false;
+               if (goal == null) {
+                       if (other.goal != null)
+                               return false;
+               } else if (!goal.equals(other.goal))
+                       return false;
+               if (id == null) {
+                       if (other.id != null)
+                               return false;
+               } else if (!id.equals(other.id))
+                       return false;
+               if (isActive == null) {
+                       if (other.isActive != null)
+                               return false;
+               } else if (!isActive.equals(other.isActive))
+                       return false;
+               if (repetitions == null) {
+                       if (other.repetitions != null)
+                               return false;
+               } else if (!repetitions.equals(other.repetitions))
+                       return false;
+               if (repetitionsOfSet == null) {
+                       if (other.repetitionsOfSet != null)
+                               return false;
+               } else if (!repetitionsOfSet.equals(other.repetitionsOfSet))
+                       return false;
+               if (title == null) {
+                       if (other.title != null)
+                               return false;
+               } else if (!title.equals(other.title))
+                       return false;
+               if (validSince == null) {
+                       if (other.validSince != null)
+                               return false;
+               } else if (!validSince.equals(other.validSince))
+                       return false;
+               if (validUntil == null) {
+                       if (other.validUntil != null)
+                               return false;
+               } else if (!validUntil.equals(other.validUntil))
+                       return false;
+               return true;
+       }
+
+
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("Training [id=");
+               builder.append(id);
+               builder.append(", title=");
+               builder.append(title);
+               builder.append(", description=");
+               builder.append(description);
+               builder.append(", repetitionsOfSet=");
+               builder.append(repetitionsOfSet);
+               builder.append(", repetitions=");
+               builder.append(repetitions);
+               builder.append(", factorOfFmax=");
+               builder.append(factorOfFmax);
+               builder.append(", created=");
+               builder.append(created);
+               builder.append(", validSince=");
+               builder.append(validSince);
+               builder.append(", validUntil=");
+               builder.append(validUntil);
+               builder.append(", isActive=");
+               builder.append(isActive);
+               builder.append(", goal=");
+               builder.append(goal);
+               builder.append(", athlet=");
+               builder.append(athlet);
+               builder.append(", exercise=");
+               builder.append(exercise);
+               builder.append("]");
+               return builder.toString();
+       }
+    
+    
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewAllExercises.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewAllExercises.java
new file mode 100644 (file)
index 0000000..e3a38e7
--- /dev/null
@@ -0,0 +1,26 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.math.BigDecimal;
+
+public interface ViewAllExercises {
+    Long getIdExercise();
+    String getNameEnglish();
+    String getDescriptionEnglish();
+    String getNameGerman();
+    String getDescriptionGerman();
+    String getUrlVideo();
+    Long getIdTool();
+    String getToolnameEnglish();
+    String getToolnameGerman();
+    BigDecimal getMinWeight();
+    BigDecimal getMaxWeight();
+    BigDecimal getStepsWeight();
+    String getDescription();
+    Long getId();
+    String getNameLatin();
+    String getMuscleNameEnglish();
+    String getMuscleNameGerman();
+    String getMovementEnglish();
+    String getMovementGerman();
+    Long getFkMusclegroup();
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewMusclegroups.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewMusclegroups.java
new file mode 100644 (file)
index 0000000..9502d41
--- /dev/null
@@ -0,0 +1,7 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+public interface ViewMusclegroups {
+    Long getId();
+    String getNameEnglish();
+    String getNameGerman();
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewMuscles.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewMuscles.java
new file mode 100644 (file)
index 0000000..0f95df5
--- /dev/null
@@ -0,0 +1,11 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+public interface ViewMuscles {
+    Long getId();
+    String getNameLatin();
+    String getNameEnglish();
+    String getNameGerman();
+    String getMovementEnglish();
+    String getMovementGerman();
+    Long getFkMusclegroup();
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewProtokollSetsOfToday.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewProtokollSetsOfToday.java
new file mode 100644 (file)
index 0000000..3f66e54
--- /dev/null
@@ -0,0 +1,10 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.time.LocalDateTime;
+
+public interface ViewProtokollSetsOfToday {
+    Long getFkUser();
+    Long getFkExercise();
+    LocalDateTime getTrainingdate();
+    Integer getNumberOfSet();
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewTools.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewTools.java
new file mode 100644 (file)
index 0000000..706bd15
--- /dev/null
@@ -0,0 +1,13 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.math.BigDecimal;
+
+public interface ViewTools {
+    Long getId();
+    String getToolnameEnglish();
+    String getToolnameGerman();
+    BigDecimal getMinWeight();
+    BigDecimal getStepsWeight();
+    BigDecimal getMaxWeight();
+    String getDescription();
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewToolweights.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewToolweights.java
new file mode 100644 (file)
index 0000000..6b03299
--- /dev/null
@@ -0,0 +1,10 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+import java.math.BigDecimal;
+
+public interface ViewToolweights {
+    Long getId();
+    BigDecimal getMinWeight();
+    BigDecimal getStepsWeight();
+    BigDecimal getMaxWeight();
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewUsers.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/model/ViewUsers.java
new file mode 100644 (file)
index 0000000..e249319
--- /dev/null
@@ -0,0 +1,9 @@
+package com.triathlon_coaching.product.athletik.backend.model;
+
+public interface ViewUsers {
+    Long getId();
+    String getUsername();
+    String getNameUser();
+    String getEmail();
+    String getPass();
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/AthletRepository.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/AthletRepository.java
new file mode 100644 (file)
index 0000000..3302dcb
--- /dev/null
@@ -0,0 +1,44 @@
+package com.triathlon_coaching.product.athletik.backend.repo;
+
+import com.triathlon_coaching.product.athletik.backend.model.Athlet;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.Optional;
+
+@Repository
+public interface AthletRepository extends JpaRepository<Athlet, Long> {
+    
+    Optional<Athlet> findByUsername(String username);
+    
+    Optional<Athlet> findByEmail(String email);
+    
+    Boolean existsByUsername(String username);
+    
+    Boolean existsByEmail(String email);
+    
+    @Query("SELECT a FROM Athlet a JOIN a.gyms g WHERE g.id = :gymId")
+    java.util.List<Athlet> findByGymId(@Param("gymId") Long gymId);
+    
+    java.util.List<Athlet> findByIsActive(Boolean isActive);
+    
+    java.util.List<Athlet> findByFullnameContainingIgnoreCase(String name);
+    
+    @Query("SELECT a FROM Athlet a WHERE a.lastLogin < :date")
+    java.util.List<Athlet> findInactiveSince(@Param("date") LocalDateTime date);
+    
+    @Transactional
+    @Modifying
+    @Query("UPDATE Athlet a SET a.lastLogin = :lastLogin WHERE a.id = :id")
+    void updateLastLogin(@Param("id") Long id, @Param("lastLogin") LocalDateTime lastLogin);
+    
+    @Transactional
+    @Modifying
+    @Query("UPDATE Athlet a SET a.isActive = :isActive WHERE a.id = :id")
+    void updateActiveStatus(@Param("id") Long id, @Param("isActive") Boolean isActive);
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/ExerciseRepository.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/ExerciseRepository.java
new file mode 100644 (file)
index 0000000..2b5daa7
--- /dev/null
@@ -0,0 +1,18 @@
+package com.triathlon_coaching.product.athletik.backend.repo;
+
+import com.triathlon_coaching.product.athletik.backend.model.Exercise;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface ExerciseRepository extends JpaRepository<Exercise, Long> {
+    @Query("SELECT e FROM Exercise e JOIN e.muscles m WHERE m.musclegroupNameEnglish = :muscleGroup")
+    List<Exercise> findByMuscleGroup(@Param("muscleGroup") String muscleGroup);
+    
+    @Query("SELECT e FROM Exercise e JOIN e.muscles m WHERE m.id = :muscleId")
+    List<Exercise> findByMuscleId(@Param("muscleId") Long muscleId);
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/GoalRepository.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/GoalRepository.java
new file mode 100644 (file)
index 0000000..aaa4803
--- /dev/null
@@ -0,0 +1,29 @@
+package com.triathlon_coaching.product.athletik.backend.repo;
+
+import com.triathlon_coaching.product.athletik.backend.model.Goal;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface GoalRepository extends JpaRepository<Goal, Long> {
+    
+    Optional<Goal> findByGoalname(String goalname);
+    
+    List<Goal> findByGoalnameContainingIgnoreCase(String goalname);
+    
+    List<Goal> findByDescriptionContainingIgnoreCase(String description);
+    
+    @Query("SELECT g FROM Goal g JOIN g.trainings t WHERE t.athlet.id = :athletId")
+    List<Goal> findByAthletId(@Param("athletId") Long athletId);
+    
+    @Query("SELECT g FROM Goal g JOIN g.trainings t WHERE t.exercise.id = :exerciseId")
+    List<Goal> findByExerciseId(@Param("exerciseId") Long exerciseId);
+    
+    @Query("SELECT DISTINCT g FROM Goal g WHERE g.id NOT IN (SELECT t.goal.id FROM Training t WHERE t.athlet.id = :athletId)")
+    List<Goal> findAvailableForAthlet(@Param("athletId") Long athletId);
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/GymRepository.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/GymRepository.java
new file mode 100644 (file)
index 0000000..2143a5f
--- /dev/null
@@ -0,0 +1,21 @@
+package com.triathlon_coaching.product.athletik.backend.repo;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import com.triathlon_coaching.product.athletik.backend.model.Gym;
+
+@Repository
+public interface GymRepository extends JpaRepository<Gym, Long> {
+    
+    @Query("SELECT g FROM Gym g JOIN g.athleten a WHERE a.id = :athletId")
+    List<Gym> findByAthletenId(@Param("athletId") Long athletId);
+    
+    List<Gym> findByGymnameContainingIgnoreCase(String name);
+    
+    List<Gym> findByGymlocationContainingIgnoreCase(String location);
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/LogRepository.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/LogRepository.java
new file mode 100644 (file)
index 0000000..b83465e
--- /dev/null
@@ -0,0 +1,58 @@
+package com.triathlon_coaching.product.athletik.backend.repo;
+
+import com.triathlon_coaching.product.athletik.backend.model.Log;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Repository
+public interface LogRepository extends JpaRepository<Log, Long> {
+    
+    List<Log> findByAthletIdOrderByCreatedDesc(Long athletId);
+    
+    Page<Log> findByAthletIdOrderByCreatedDesc(Long athletId, Pageable pageable);
+    
+    List<Log> findBySid(String sid);
+    
+    List<Log> findByScriptContainingIgnoreCase(String script);
+    
+    List<Log> findByLogLevel(String logLevel);
+    
+    List<Log> findByLogdataContainingIgnoreCase(String logdata);
+    
+    List<Log> findByCreatedBetween(LocalDateTime start, LocalDateTime end);
+    
+    List<Log> findByCreatedBefore(LocalDateTime date);
+    
+    @Query("SELECT l FROM Log l WHERE l.athlet.id = :athletId AND l.created BETWEEN :start AND :end ORDER BY l.created DESC")
+    List<Log> findByAthletIdAndCreatedBetween(@Param("athletId") Long athletId, 
+                                             @Param("start") LocalDateTime start, 
+                                             @Param("end") LocalDateTime end);
+    
+    @Query("SELECT l FROM Log l WHERE l.athlet.id = :athletId AND l.logLevel = :logLevel ORDER BY l.created DESC")
+    List<Log> findByAthletIdAndLogLevel(@Param("athletId") Long athletId, 
+                                       @Param("logLevel") String logLevel);
+    
+    @Query("SELECT l FROM Log l WHERE l.athlet.id = :athletId AND LOWER(l.logdata) LIKE LOWER(CONCAT('%', :searchTerm, '%')) ORDER BY l.created DESC")
+    List<Log> findByAthletIdAndLogdataContaining(@Param("athletId") Long athletId, 
+                                                @Param("searchTerm") String searchTerm);
+    
+    @Query("SELECT COUNT(l) FROM Log l WHERE l.athlet.id = :athletId AND l.created >= :since")
+    Long countByAthletIdSince(@Param("athletId") Long athletId, 
+                             @Param("since") LocalDateTime since);
+    
+    @Query("SELECT DISTINCT l.script FROM Log l WHERE l.athlet.id = :athletId")
+    List<String> findDistinctScriptsByAthletId(@Param("athletId") Long athletId);
+    
+    @Query("SELECT DISTINCT l.logLevel FROM Log l WHERE l.athlet.id = :athletId")
+    List<String> findDistinctLogLevelsByAthletId(@Param("athletId") Long athletId);
+    
+    @Query("SELECT l FROM Log l WHERE l.athlet.id = :athletId AND l.created = (SELECT MAX(l2.created) FROM Log l2 WHERE l2.athlet.id = :athletId)")
+    Log findLatestByAthletId(@Param("athletId") Long athletId);
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/MuscleRepository.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/MuscleRepository.java
new file mode 100644 (file)
index 0000000..c6ffa3f
--- /dev/null
@@ -0,0 +1,34 @@
+package com.triathlon_coaching.product.athletik.backend.repo;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import com.triathlon_coaching.product.athletik.backend.model.Muscle;
+
+@Repository
+public interface MuscleRepository extends JpaRepository<Muscle, Long> {
+    
+    @Query("SELECT DISTINCT m.musclegroupNameEnglish FROM Muscle m WHERE m.musclegroupNameEnglish IS NOT NULL")
+    List<String> findAllMuscleGroups();
+    
+    @Query("SELECT m FROM Muscle m WHERE m.musclegroupNameEnglish = :group OR m.musclegroupNameGerman = :group")
+    List<Muscle> findByMuscleGroup(@Param("group") String group);
+    
+    @Query("SELECT m FROM Muscle m JOIN m.exercises e WHERE e.id = :exerciseId")
+    List<Muscle> findByExercisesId(@Param("exerciseId") Long exerciseId);
+    
+    List<Muscle> findByMuscleNameLatinContainingIgnoreCase(String name);
+    
+    List<Muscle> findByMuscleNameEnglishContainingIgnoreCase(String name);
+    
+    List<Muscle> findByMuscleNameGermanContainingIgnoreCase(String name);
+    
+    @Query("SELECT m FROM Muscle m WHERE LOWER(m.movementEnglish) LIKE LOWER(CONCAT('%', :movement, '%')) OR LOWER(m.movementGerman) LIKE LOWER(CONCAT('%', :movement, '%'))")
+    List<Muscle> findByMovementContainingIgnoreCase(@Param("movement") String movement);
+
+       List<Muscle> findByMusclegroupNameEnglishOrMusclegroupNameGerman(String muscleGroup, String muscleGroup2);
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/ProtokollRepository.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/ProtokollRepository.java
new file mode 100644 (file)
index 0000000..74a58fd
--- /dev/null
@@ -0,0 +1,55 @@
+package com.triathlon_coaching.product.athletik.backend.repo;
+
+import com.triathlon_coaching.product.athletik.backend.model.Protokoll;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Repository
+public interface ProtokollRepository extends JpaRepository<Protokoll, Long> {
+    
+    List<Protokoll> findByAthletIdOrderByTrainingDateDesc(Long athletId);
+    
+    List<Protokoll> findByAthletIdAndExerciseIdOrderByTrainingDateDesc(Long athletId, Long exerciseId);
+    
+    List<Protokoll> findByAthletIdAndTrainingDateBetween(Long athletId, LocalDateTime start, LocalDateTime end);
+    
+    List<Protokoll> findByAthletIdAndIsTest(Long athletId, Boolean isTest);
+    
+    List<Protokoll> findByAthletIdAndExerciseIdAndIsTest(Long athletId, Long exerciseId, Boolean isTest);
+    
+    @Query("SELECT p FROM Protokoll p WHERE p.athlet.id = :athletId AND DATE(p.trainingDate) = CURRENT_DATE")
+    List<Protokoll> findTodaySessionsByAthletId(@Param("athletId") Long athletId);
+    
+    @Query("SELECT MAX(p.numberOfSet) FROM Protokoll p WHERE p.athlet.id = :athletId AND p.exercise.id = :exerciseId AND DATE(p.trainingDate) = CURRENT_DATE")
+    Integer findMaxSetNumberForToday(@Param("athletId") Long athletId, @Param("exerciseId") Long exerciseId);
+    
+    @Query("SELECT p FROM Protokoll p WHERE p.athlet.id = :athletId AND p.exercise.id = :exerciseId AND p.isTest = true ORDER BY p.trainingDate DESC")
+    List<Protokoll> findLatestTestsByAthletAndExercise(@Param("athletId") Long athletId, @Param("exerciseId") Long exerciseId);
+    
+    @Query("SELECT p FROM Protokoll p WHERE p.athlet.id = :athletId AND p.exercise.id = :exerciseId AND p.isTest = false ORDER BY p.trainingDate DESC")
+    List<Protokoll> findLatestTrainingsByAthletAndExercise(@Param("athletId") Long athletId, @Param("exerciseId") Long exerciseId);
+    
+    @Query("SELECT p FROM Protokoll p WHERE p.athlet.id = :athletId AND p.trainingDate = (SELECT MAX(p2.trainingDate) FROM Protokoll p2 WHERE p2.athlet.id = :athletId AND p2.exercise.id = p.exercise.id AND p2.isTest = true)")
+    List<Protokoll> findLatestTestForEachExercise(@Param("athletId") Long athletId);
+    
+    @Query("SELECT p FROM Protokoll p WHERE p.athlet.id = :athletId AND p.exercise.id = :exerciseId ORDER BY p.trainingDate DESC, p.numberOfSet DESC")
+    Page<Protokoll> findByAthletIdAndExerciseIdPaged(@Param("athletId") Long athletId, @Param("exerciseId") Long exerciseId, Pageable pageable);
+    
+    @Query("SELECT SUM(p.volume) FROM Protokoll p WHERE p.athlet.id = :athletId AND p.trainingDate BETWEEN :start AND :end")
+    Double findTotalVolumeByAthletIdAndDateRange(@Param("athletId") Long athletId, @Param("start") LocalDateTime start, @Param("end") LocalDateTime end);
+    
+    @Query("SELECT p FROM Protokoll p WHERE p.athlet.id = :athletId AND p.weight >= :minWeight")
+    List<Protokoll> findByAthletIdAndMinWeight(@Param("athletId") Long athletId, @Param("minWeight") Double minWeight);
+    
+    @Query("SELECT p FROM Protokoll p WHERE p.athlet.id = :athletId AND p.oneRepMax IS NOT NULL ORDER BY p.oneRepMax DESC")
+    List<Protokoll> findPersonalRecords(@Param("athletId") Long athletId);
+    
+    Long countByAthletIdAndTrainingDateAfter(Long athletId, LocalDateTime date);
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/ToolRepository.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/ToolRepository.java
new file mode 100644 (file)
index 0000000..28d4e2f
--- /dev/null
@@ -0,0 +1,37 @@
+package com.triathlon_coaching.product.athletik.backend.repo;
+
+import com.triathlon_coaching.product.athletik.backend.model.Tool;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+
+import com.triathlon_coaching.product.athletik.backend.model.Tool;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface ToolRepository extends JpaRepository<Tool, Long> {
+    
+    @Query("SELECT t FROM Tool t JOIN t.gyms g WHERE g.id = :gymId")
+    List<Tool> findByGymsId(@Param("gymId") Long gymId);
+    
+    List<Tool> findByToolnameEnglishContainingIgnoreCase(String name);
+    
+    List<Tool> findByToolnameGermanContainingIgnoreCase(String name);
+    
+    @Query("SELECT t FROM Tool t WHERE t.minWeight <= :weight AND t.maxWeight >= :weight")
+    List<Tool> findByWeightRange(@Param("weight") Double weight);
+    
+    @Query("SELECT t FROM Tool t WHERE t.minWeight <= :weight AND t.maxWeight >= :weight AND :weight % t.stepsWeight = 0")
+    List<Tool> findByWeightAndStep(@Param("weight") Double weight);
+    
+    List<Tool> findByMinWeightGreaterThanEqual(Double minWeight);
+    
+    List<Tool> findByMaxWeightLessThanEqual(Double maxWeight);
+    
+    List<Tool> findByMinWeightLessThanEqualAndMaxWeightGreaterThanEqual(Double minWeight, Double maxWeight);
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/TrainingRepository.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/repo/TrainingRepository.java
new file mode 100644 (file)
index 0000000..3d352ef
--- /dev/null
@@ -0,0 +1,43 @@
+package com.triathlon_coaching.product.athletik.backend.repo;
+
+import com.triathlon_coaching.product.athletik.backend.model.Training;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface TrainingRepository extends JpaRepository<Training, Long> {
+    
+    List<Training> findByAthletIdOrderByCreatedDesc(Long athletId);
+    
+    List<Training> findByAthletIdAndIsActiveTrue(Long athletId);
+    
+    List<Training> findByExerciseId(Long exerciseId);
+    
+    List<Training> findByGoalId(Long goalId);
+    
+    @Query("SELECT t FROM Training t WHERE t.athlet.id = :athletId AND t.isActive = true AND " +
+           "(t.validSince IS NULL OR t.validSince <= :now) AND " +
+           "(t.validUntil IS NULL OR t.validUntil >= :now)")
+    List<Training> findActiveByAthletId(@Param("athletId") Long athletId, @Param("now") LocalDateTime now);
+    
+    @Query("SELECT t FROM Training t WHERE t.athlet.id = :athletId AND t.exercise.id = :exerciseId AND t.isActive = true")
+    List<Training> findByAthletIdAndExerciseId(@Param("athletId") Long athletId, @Param("exerciseId") Long exerciseId);
+    
+    @Query("SELECT t FROM Training t WHERE t.athlet.id = :athletId AND t.goal.id = :goalId AND t.isActive = true")
+    List<Training> findByAthletIdAndGoalId(@Param("athletId") Long athletId, @Param("goalId") Long goalId);
+    
+    @Query("SELECT t FROM Training t WHERE t.created BETWEEN :start AND :end")
+    List<Training> findByCreatedBetween(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end);
+    
+    @Query("SELECT t FROM Training t WHERE t.validSince <= :date AND (t.validUntil IS NULL OR t.validUntil >= :date)")
+    List<Training> findValidAt(@Param("date") LocalDateTime date);
+    
+    Optional<Training> findByAthletIdAndExerciseIdAndRepetitionsOfSetAndRepetitionsAndFactorOfFmax(
+            Long athletId, Long exerciseId, Integer repetitionsOfSet, Integer repetitions, Double factorOfFmax);
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/AthletService.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/AthletService.java
new file mode 100644 (file)
index 0000000..97d1c3e
--- /dev/null
@@ -0,0 +1,120 @@
+package com.triathlon_coaching.product.athletik.backend.service;
+
+import com.triathlon_coaching.product.athletik.backend.model.Athlet;
+import com.triathlon_coaching.product.athletik.backend.repo.AthletRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class AthletService {
+    
+    @Autowired
+    private AthletRepository athletRepository;
+    
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+    
+    @Autowired
+    private EmailService emailService;
+    
+    public List<Athlet> findAllAthleten() {
+        return athletRepository.findAll();
+    }
+    
+    public Optional<Athlet> findAthletById(Long id) {
+        return athletRepository.findById(id);
+    }
+    
+    public Optional<Athlet> findAthletByUsername(String username) {
+        return athletRepository.findByUsername(username);
+    }
+    
+    public Optional<Athlet> findAthletByEmail(String email) {
+        return athletRepository.findByEmail(email);
+    }
+    
+    public List<Athlet> findAthletenByGymId(Long gymId) {
+        return athletRepository.findByGymId(gymId);
+    }
+    
+    public boolean existsByUsername(String username) {
+        return athletRepository.existsByUsername(username);
+    }
+    
+    public boolean existsByEmail(String email) {
+        return athletRepository.existsByEmail(email);
+    }
+    
+    public Athlet saveAthlet(Athlet athlet) {
+        return athletRepository.save(athlet);
+    }
+    
+    @Transactional
+    public Athlet registerAthlet(Athlet athlet) {
+        // Passwort hashen
+        athlet.setPass(passwordEncoder.encode(athlet.getPass()));
+        Athlet savedAthlet = athletRepository.save(athlet);
+        
+        // Willkommens-E-Mail senden
+        emailService.sendWelcomeEmail(savedAthlet);
+        
+        return savedAthlet;
+    }
+    
+    public Optional<Athlet> authenticate(String username, String password) {
+        Optional<Athlet> athletOptional = athletRepository.findByUsername(username);
+        if (athletOptional.isPresent()) {
+            Athlet athlet = athletOptional.get();
+            if (passwordEncoder.matches(password, athlet.getPass())) {
+                // Last Login aktualisieren
+                athlet.setLastLogin(LocalDateTime.now());
+                athletRepository.save(athlet);
+                return Optional.of(athlet);
+            }
+        }
+        return Optional.empty();
+    }
+    
+    @Transactional
+    public boolean updatePassword(Long id, String currentPassword, String newPassword) {
+        Optional<Athlet> athletOptional = athletRepository.findById(id);
+        if (athletOptional.isPresent()) {
+            Athlet athlet = athletOptional.get();
+            if (passwordEncoder.matches(currentPassword, athlet.getPass())) {
+                athlet.setPass(passwordEncoder.encode(newPassword));
+                athletRepository.save(athlet);
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    public void deleteAthlet(Long id) {
+        athletRepository.deleteById(id);
+    }
+    
+    @Transactional
+    public void deactivateAthlet(Long id) {
+        athletRepository.updateActiveStatus(id, false);
+    }
+    
+    @Transactional
+    public void activateAthlet(Long id) {
+        athletRepository.updateActiveStatus(id, true);
+    }
+    
+    @Transactional
+    public void updateLastLogin(Long id) {
+        athletRepository.updateLastLogin(id, LocalDateTime.now());
+    }
+    
+    public List<Athlet> findInactiveAthletenSince(LocalDateTime date) {
+        return athletRepository.findInactiveSince(date);
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/EmailService.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/EmailService.java
new file mode 100644 (file)
index 0000000..e7b3efb
--- /dev/null
@@ -0,0 +1,119 @@
+package com.triathlon_coaching.product.athletik.backend.service;
+
+import com.triathlon_coaching.product.athletik.backend.model.Athlet;
+import com.triathlon_coaching.product.athletik.backend.model.Protokoll;
+import com.triathlon_coaching.product.athletik.backend.repo.AthletRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.context.Context;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+
+@Service
+public class EmailService {
+
+    @Autowired
+    private JavaMailSender mailSender;
+    
+    @Autowired
+    private TemplateEngine templateEngine;
+    
+    @Autowired
+    private AthletRepository athletRepository;
+    
+    @Async
+    public void sendTrainingSummary(Long athletId, List<Protokoll> trainingData) {
+        Optional<Athlet> athletOptional = athletRepository.findById(athletId);
+        if (!athletOptional.isPresent()) {
+            return;
+        }
+        
+        Athlet athlet = athletOptional.get();
+        String emailContent = generateTrainingSummaryEmail(athlet, trainingData);
+        
+        try {
+            MimeMessage message = mailSender.createMimeMessage();
+            MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
+            
+            helper.setTo(athlet.getEmail());
+            helper.setSubject("Ihr Training vom " + LocalDate.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy")));
+            helper.setText(emailContent, true);
+            
+            mailSender.send(message);
+        } catch (MessagingException e) {
+            // Logging des Fehlers
+            System.err.println("Fehler beim Senden der E-Mail: " + e.getMessage());
+        }
+    }
+    
+    private String generateTrainingSummaryEmail(Athlet athlet, List<Protokoll> trainingData) {
+        Context context = new Context(Locale.GERMAN);
+        context.setVariable("athlet", athlet);
+        context.setVariable("trainingData", trainingData);
+        context.setVariable("trainingDate", LocalDate.now());
+      //  context.setVariable("totalVolume", calculateTotalVolume(trainingData));
+        context.setVariable("totalSets", trainingData.size());
+        
+        return templateEngine.process("email/training-summary", context);
+    }
+
+    /*
+    private double calculateTotalVolume(List<Protokoll> trainingData) {
+        return trainingData.stream()
+                .mapToDouble(p -> p.getWeight() * p.getRepetitions())
+                .sum();
+    }
+    */
+    @Async
+    public void sendWelcomeEmail(Athlet athlet) {
+        Context context = new Context(Locale.GERMAN);
+        context.setVariable("athlet", athlet);
+        
+        String emailContent = templateEngine.process("email/welcome", context);
+        
+        try {
+            MimeMessage message = mailSender.createMimeMessage();
+            MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
+            
+            helper.setTo(athlet.getEmail());
+            helper.setSubject("Willkommen bei Athletik Training!");
+            helper.setText(emailContent, true);
+            
+            mailSender.send(message);
+        } catch (MessagingException e) {
+            System.err.println("Fehler beim Senden der Willkommens-E-Mail: " + e.getMessage());
+        }
+    }
+    
+    @Async
+    public void sendPasswordResetEmail(Athlet athlet, String resetToken) {
+        Context context = new Context(Locale.GERMAN);
+        context.setVariable("athlet", athlet);
+        context.setVariable("resetToken", resetToken);
+        
+        String emailContent = templateEngine.process("email/password-reset", context);
+        
+        try {
+            MimeMessage message = mailSender.createMimeMessage();
+            MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
+            
+            helper.setTo(athlet.getEmail());
+            helper.setSubject("Passwort zurücksetzen - Athletik Training");
+            helper.setText(emailContent, true);
+            
+            mailSender.send(message);
+        } catch (MessagingException e) {
+            System.err.println("Fehler beim Senden der Passwort-Reset-E-Mail: " + e.getMessage());
+        }
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/ExerciseService.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/ExerciseService.java
new file mode 100644 (file)
index 0000000..e55389e
--- /dev/null
@@ -0,0 +1,89 @@
+package com.triathlon_coaching.product.athletik.backend.service;
+
+import com.triathlon_coaching.product.athletik.backend.model.Exercise;
+import com.triathlon_coaching.product.athletik.backend.model.Muscle;
+import com.triathlon_coaching.product.athletik.backend.repo.ExerciseRepository;
+import com.triathlon_coaching.product.athletik.backend.repo.MuscleRepository;
+import com.triathlon_coaching.product.athletik.backend.repo.ToolRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class ExerciseService {
+
+    @Autowired
+    private ExerciseRepository exerciseRepository;
+    
+    @Autowired
+    private MuscleRepository muscleRepository;
+    
+    @Autowired
+    private ToolRepository toolRepository;
+
+    public List<Exercise> findAllExercises() {
+        return exerciseRepository.findAll();
+    }
+
+    public Optional<Exercise> findExerciseById(Long id) {
+        return exerciseRepository.findById(id);
+    }
+
+    public List<Exercise> findExercisesByMuscleGroup(String muscleGroup) {
+        return exerciseRepository.findByMuscleGroup(muscleGroup);
+    }
+
+    public List<Exercise> findExercisesByMuscleId(Long muscleId) {
+        return exerciseRepository.findByMuscleId(muscleId);
+    }
+
+    public List<String> findAllMuscleGroups() {
+        return muscleRepository.findAllMuscleGroups();
+    }
+
+    public List<Muscle> findAllMuscles() {
+        return muscleRepository.findAll();
+    }
+
+    public List<Muscle> findMusclesByGroup(String muscleGroup) {
+        return muscleRepository.findByMusclegroupNameEnglishOrMusclegroupNameGerman(muscleGroup, muscleGroup);
+    }
+
+    public List<Double> getWeightsForTool(Long toolId) {
+        return toolRepository.findById(toolId)
+                .map(tool -> {
+                    List<Double> weights = new ArrayList<>();
+                    double current = tool.getMinWeight();
+                    while (current <= tool.getMaxWeight()) {
+                        weights.add(current);
+                        current += tool.getStepsWeight();
+                    }
+                    return weights;
+                })
+                .orElse(Collections.emptyList());
+    }
+
+    public Map<String, Object> getExerciseSummary() {
+        Map<String, Object> summary = new HashMap<>();
+        
+        List<String> muscleGroups = findAllMuscleGroups();
+        summary.put("muscleGroups", muscleGroups);
+        
+        Map<String, List<Muscle>> musclesByGroup = new HashMap<>();
+        for (String group : muscleGroups) {
+            musclesByGroup.put(group, findMusclesByGroup(group));
+        }
+        summary.put("musclesByGroup", musclesByGroup);
+        
+        Map<Long, List<Exercise>> exercisesByMuscle = new HashMap<>();
+        List<Muscle> allMuscles = findAllMuscles();
+        for (Muscle muscle : allMuscles) {
+            exercisesByMuscle.put(muscle.getId(), findExercisesByMuscleId(muscle.getId()));
+        }
+        summary.put("exercisesByMuscle", exercisesByMuscle);
+        
+        return summary;
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/GoalService.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/GoalService.java
new file mode 100644 (file)
index 0000000..3b30d93
--- /dev/null
@@ -0,0 +1,64 @@
+package com.triathlon_coaching.product.athletik.backend.service;
+
+import com.triathlon_coaching.product.athletik.backend.model.Goal;
+import com.triathlon_coaching.product.athletik.backend.repo.GoalRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class GoalService {
+    
+    @Autowired
+    private GoalRepository goalRepository;
+    
+    public List<Goal> findAllGoals() {
+        return goalRepository.findAll();
+    }
+    
+    public Optional<Goal> findGoalById(Long id) {
+        return goalRepository.findById(id);
+    }
+    
+    public Optional<Goal> findGoalByName(String goalname) {
+        return goalRepository.findByGoalname(goalname);
+    }
+    
+    public List<Goal> findGoalsByAthletId(Long athletId) {
+        return goalRepository.findByAthletId(athletId);
+    }
+    
+    public List<Goal> findGoalsByExerciseId(Long exerciseId) {
+        return goalRepository.findByExerciseId(exerciseId);
+    }
+    
+    public List<Goal> findAvailableGoalsForAthlet(Long athletId) {
+        return goalRepository.findAvailableForAthlet(athletId);
+    }
+    
+    public List<Goal> searchGoals(String searchTerm) {
+        List<Goal> byName = goalRepository.findByGoalnameContainingIgnoreCase(searchTerm);
+        List<Goal> byDescription = goalRepository.findByDescriptionContainingIgnoreCase(searchTerm);
+        
+        // Duplikate entfernen und kombinieren
+        byName.addAll(byDescription.stream()
+                .filter(goal -> !byName.contains(goal))
+                .toList());
+        
+        return byName;
+    }
+    
+    public Goal saveGoal(Goal goal) {
+        return goalRepository.save(goal);
+    }
+    
+    public void deleteGoal(Long id) {
+        goalRepository.deleteById(id);
+    }
+    
+    public boolean existsByName(String goalname) {
+        return goalRepository.findByGoalname(goalname).isPresent();
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/GymService.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/GymService.java
new file mode 100644 (file)
index 0000000..f5c12aa
--- /dev/null
@@ -0,0 +1,37 @@
+package com.triathlon_coaching.product.athletik.backend.service;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.triathlon_coaching.product.athletik.backend.model.Gym;
+import com.triathlon_coaching.product.athletik.backend.repo.GymRepository;
+
+@Service
+public class GymService {
+    
+    @Autowired
+    private GymRepository gymRepository;
+    
+    public List<Gym> findAllGyms() {
+        return gymRepository.findAll();
+    }
+    
+    public Optional<Gym> findGymById(Long id) {
+        return gymRepository.findById(id);
+    }
+    
+    public List<Gym> findGymsByAthletId(Long athletId) {
+        return gymRepository.findByAthletenId(athletId);
+    }
+    
+    public Gym saveGym(Gym gym) {
+        return gymRepository.save(gym);
+    }
+    
+    public void deleteGym(Long id) {
+        gymRepository.deleteById(id);
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/LogService.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/LogService.java
new file mode 100644 (file)
index 0000000..c8be356
--- /dev/null
@@ -0,0 +1,132 @@
+package com.triathlon_coaching.product.athletik.backend.service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import com.triathlon_coaching.product.athletik.backend.model.Log;
+import com.triathlon_coaching.product.athletik.backend.repo.LogRepository;
+
+@Service
+public class LogService {
+
+       @Autowired
+       private LogRepository logRepository;
+
+       @Autowired
+       private AthletService athletService;
+
+       public List<Log> findAllLogs() {
+               return logRepository.findAll();
+       }
+
+       public List<Log> findByAthletId(Long athletId) {
+               return logRepository.findByAthletIdOrderByCreatedDesc(athletId);
+       }
+
+       public Page<Log> findByAthletId(Long athletId, Pageable pageable) {
+               return logRepository.findByAthletIdOrderByCreatedDesc(athletId, pageable);
+       }
+
+       public List<Log> findByAthletIdAndDateRange(Long athletId, LocalDateTime start, LocalDateTime end) {
+               return logRepository.findByAthletIdAndCreatedBetween(athletId, start, end);
+       }
+
+       public List<Log> findByAthletIdAndLogLevel(Long athletId, String logLevel) {
+               return logRepository.findByAthletIdAndLogLevel(athletId, logLevel);
+       }
+
+       public List<Log> searchByAthletIdAndLogdata(Long athletId, String searchTerm) {
+               return logRepository.findByAthletIdAndLogdataContaining(athletId, searchTerm);
+       }
+
+       public Log saveLog(Log log) {
+               return logRepository.save(log);
+       }
+
+       public void deleteLog(Long id) {
+               logRepository.deleteById(id);
+       }
+
+       public void deleteOldLogs(LocalDateTime before) {
+               List<Log> oldLogs = logRepository.findByCreatedBefore(before);
+               logRepository.deleteAll(oldLogs);
+       }
+
+       // Hilfsmethoden für das Logging
+       public void logInfo(Long athletId, String logdata, String script, HttpServletRequest request) {
+               createLog(athletId, logdata, "INFO", script, request);
+       }
+
+       public void logWarning(Long athletId, String logdata, String script, HttpServletRequest request) {
+               createLog(athletId, logdata, "WARNING", script, request);
+       }
+
+       public void logError(Long athletId, String logdata, String script, HttpServletRequest request) {
+               createLog(athletId, logdata, "ERROR", script, request);
+       }
+
+       public void logDebug(Long athletId, String logdata, String script, HttpServletRequest request) {
+               createLog(athletId, logdata, "DEBUG", script, request);
+       }
+
+       private void createLog(Long athletId, String logdata, String logLevel, String script, HttpServletRequest request) {
+               athletService.findAthletById(athletId).ifPresent(athlet -> {
+                       Log log = new Log();
+                       log.setLogdata(logdata);
+                       log.setLogLevel(logLevel);
+                       log.setScript(script);
+                       log.setAthlet(athlet);
+
+                       if (request != null) {
+                               log.setSid(request.getSession().getId());
+                               log.setIpAddress(getClientIpAddress(request));
+                               log.setUserAgent(request.getHeader("User-Agent"));
+                       }
+
+                       logRepository.save(log);
+               });
+       }
+
+       private String getClientIpAddress(HttpServletRequest request) {
+               String ip = request.getHeader("X-Forwarded-For");
+               if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
+                       ip = request.getHeader("Proxy-Client-IP");
+               }
+               if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
+                       ip = request.getHeader("WL-Proxy-Client-IP");
+               }
+               if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
+                       ip = request.getHeader("HTTP_CLIENT_IP");
+               }
+               if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
+                       ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+               }
+               if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
+                       ip = request.getRemoteAddr();
+               }
+               return ip;
+       }
+
+       public Long getLogCountSince(Long athletId, LocalDateTime since) {
+               return logRepository.countByAthletIdSince(athletId, since);
+       }
+
+       public List<String> getDistinctScripts(Long athletId) {
+               return logRepository.findDistinctScriptsByAthletId(athletId);
+       }
+
+       public List<String> getDistinctLogLevels(Long athletId) {
+               return logRepository.findDistinctLogLevelsByAthletId(athletId);
+       }
+
+       public Log getLatestLog(Long athletId) {
+               return logRepository.findLatestByAthletId(athletId);
+       }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/MuscleService.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/MuscleService.java
new file mode 100644 (file)
index 0000000..274e969
--- /dev/null
@@ -0,0 +1,50 @@
+package com.triathlon_coaching.product.athletik.backend.service;
+
+import com.triathlon_coaching.product.athletik.backend.model.Muscle;
+import com.triathlon_coaching.product.athletik.backend.repo.MuscleRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class MuscleService {
+    
+    @Autowired
+    private MuscleRepository muscleRepository;
+    
+    public List<Muscle> findAllMuscles() {
+        return muscleRepository.findAll();
+    }
+    
+    public Optional<Muscle> findMuscleById(Long id) {
+        return muscleRepository.findById(id);
+    }
+    
+    public List<Muscle> findMusclesByGroup(String muscleGroup) {
+        return muscleRepository.findByMuscleGroup(muscleGroup);
+    }
+    
+    public List<String> findAllMuscleGroups() {
+        return muscleRepository.findAllMuscleGroups();
+    }
+    
+    public List<Muscle> findMusclesByExerciseId(Long exerciseId) {
+        return muscleRepository.findByExercisesId(exerciseId);
+    }
+    
+    public List<Muscle> findPrimaryMusclesByExerciseId(Long exerciseId) {
+        // Hier müsste die Logik für isprimary implementiert werden
+        // Vereinfacht geben wir alle Muskeln der Übung zurück
+        return muscleRepository.findByExercisesId(exerciseId);
+    }
+    
+    public Muscle saveMuscle(Muscle muscle) {
+        return muscleRepository.save(muscle);
+    }
+    
+    public void deleteMuscle(Long id) {
+        muscleRepository.deleteById(id);
+    }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/ProtokollService.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/ProtokollService.java
new file mode 100644 (file)
index 0000000..b66fce1
--- /dev/null
@@ -0,0 +1,111 @@
+package com.triathlon_coaching.product.athletik.backend.service;
+
+import com.triathlon_coaching.product.athletik.backend.model.Protokoll;
+import com.triathlon_coaching.product.athletik.backend.repo.ProtokollRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class ProtokollService {
+    
+    @Autowired
+    private ProtokollRepository protokollRepository;
+    
+    @Autowired
+    private EmailService emailService;
+    
+    public List<Protokoll> findAllProtokolls() {
+        return protokollRepository.findAll();
+    }
+    
+    public Optional<Protokoll> findProtokollById(Long id) {
+        return protokollRepository.findById(id);
+    }
+    
+    public List<Protokoll> findByAthletId(Long athletId) {
+        return protokollRepository.findByAthletIdOrderByTrainingDateDesc(athletId);
+    }
+    
+    public List<Protokoll> findByAthletIdAndExerciseId(Long athletId, Long exerciseId) {
+        return protokollRepository.findByAthletIdAndExerciseIdOrderByTrainingDateDesc(athletId, exerciseId);
+    }
+    
+    public List<Protokoll> findTodaySessionsByAthletId(Long athletId) {
+        return protokollRepository.findTodaySessionsByAthletId(athletId);
+    }
+    
+    public List<Protokoll> findByAthletIdAndDateRange(Long athletId, LocalDateTime start, LocalDateTime end) {
+        return protokollRepository.findByAthletIdAndTrainingDateBetween(athletId, start, end);
+    }
+    
+    public List<Protokoll> findTestsByAthletId(Long athletId) {
+        return protokollRepository.findByAthletIdAndIsTest(athletId, true);
+    }
+    
+    public List<Protokoll> findTrainingsByAthletId(Long athletId) {
+        return protokollRepository.findByAthletIdAndIsTest(athletId, false);
+    }
+    
+    public List<Protokoll> findLatestTestsByAthletAndExercise(Long athletId, Long exerciseId) {
+        return protokollRepository.findLatestTestsByAthletAndExercise(athletId, exerciseId);
+    }
+    
+    public List<Protokoll> findLatestTestForEachExercise(Long athletId) {
+        return protokollRepository.findLatestTestForEachExercise(athletId);
+    }
+    
+    public Integer findMaxSetNumberForToday(Long athletId, Long exerciseId) {
+        Integer maxSet = protokollRepository.findMaxSetNumberForToday(athletId, exerciseId);
+        return maxSet != null ? maxSet : 0;
+    }
+    
+    public Protokoll saveProtokoll(Protokoll protokoll) {
+        return protokollRepository.save(protokoll);
+    }
+    
+    public void deleteProtokoll(Long id) {
+        protokollRepository.deleteById(id);
+    }
+    
+    public Double calculateTotalVolume(Long athletId, LocalDateTime start, LocalDateTime end) {
+        Double totalVolume = protokollRepository.findTotalVolumeByAthletIdAndDateRange(athletId, start, end);
+        return totalVolume != null ? totalVolume : 0.0;
+    }
+    
+    public Double calculateDailyVolume(Long athletId) {
+        LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
+        LocalDateTime endOfDay = startOfDay.plusDays(1);
+        return calculateTotalVolume(athletId, startOfDay, endOfDay);
+    }
+    
+    public Double calculateWeeklyVolume(Long athletId) {
+        LocalDateTime startOfWeek = LocalDateTime.now().minusDays(7);
+        LocalDateTime now = LocalDateTime.now();
+        return calculateTotalVolume(athletId, startOfWeek, now);
+    }
+    
+    public List<Protokoll> findPersonalRecords(Long athletId) {
+        return protokollRepository.findPersonalRecords(athletId);
+    }
+    
+    public Page<Protokoll> findByAthletIdAndExerciseIdPaged(Long athletId, Long exerciseId, Pageable pageable) {
+        return protokollRepository.findByAthletIdAndExerciseIdPaged(athletId, exerciseId, pageable);
+    }
+    
+    public Long getTrainingCountSince(Long athletId, LocalDateTime since) {
+        return protokollRepository.countByAthletIdAndTrainingDateAfter(athletId, since);
+    }
+    
+    public void sendTrainingSummaryEmail(Long athletId) {
+        List<Protokoll> todayTraining = findTodaySessionsByAthletId(athletId);
+        emailService.sendTrainingSummary(athletId, todayTraining);
+    }
+
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/ToolService.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/ToolService.java
new file mode 100644 (file)
index 0000000..e719f28
--- /dev/null
@@ -0,0 +1,48 @@
+package com.triathlon_coaching.product.athletik.backend.service;
+
+import com.triathlon_coaching.product.athletik.backend.model.Tool;
+import com.triathlon_coaching.product.athletik.backend.repo.ToolRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class ToolService {
+    
+    @Autowired
+    private ToolRepository toolRepository;
+    
+    public List<Tool> findAllTools() {
+        return toolRepository.findAll();
+    }
+    
+    public Optional<Tool> findToolById(Long id) {
+        return toolRepository.findById(id);
+    }
+    
+    public List<Tool> findToolsByGymId(Long gymId) {
+        return toolRepository.findByGymsId(gymId);
+    }
+    
+    public List<Tool> findByToolnameEnglishContainingIgnoreCase(String name) {
+        return toolRepository.findByToolnameEnglishContainingIgnoreCase(name);
+    }
+    
+    public List<Tool> findByToolnameGermanContainingIgnoreCase(String name) {
+        return toolRepository.findByToolnameGermanContainingIgnoreCase(name);
+    }
+    
+    public Tool saveTool(Tool tool) {
+        return toolRepository.save(tool);
+    }
+    
+    public void deleteTool(Long id) {
+        toolRepository.deleteById(id);
+    }
+    
+    public boolean existsById(Long id) {
+        return toolRepository.existsById(id);
+    }
+}
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/TrainingService.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/service/TrainingService.java
new file mode 100644 (file)
index 0000000..879a856
--- /dev/null
@@ -0,0 +1,94 @@
+package com.triathlon_coaching.product.athletik.backend.service;
+
+import com.triathlon_coaching.product.athletik.backend.model.Training;
+import com.triathlon_coaching.product.athletik.backend.repo.TrainingRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class TrainingService {
+    
+    @Autowired
+    private TrainingRepository trainingRepository;
+    
+    public List<Training> findAllTrainings() {
+        return trainingRepository.findAll();
+    }
+    
+    public Optional<Training> findTrainingById(Long id) {
+        return trainingRepository.findById(id);
+    }
+    
+    public List<Training> findTrainingsByAthletId(Long athletId) {
+        return trainingRepository.findByAthletIdOrderByCreatedDesc(athletId);
+    }
+    
+    public List<Training> findActiveTrainingsByAthletId(Long athletId) {
+        return trainingRepository.findByAthletIdAndIsActiveTrue(athletId);
+    }
+    
+    public List<Training> findCurrentlyValidTrainingsByAthletId(Long athletId) {
+        return trainingRepository.findActiveByAthletId(athletId, LocalDateTime.now());
+    }
+    
+    public List<Training> findTrainingsByExerciseId(Long exerciseId) {
+        return trainingRepository.findByExerciseId(exerciseId);
+    }
+    
+    public List<Training> findTrainingsByGoalId(Long goalId) {
+        return trainingRepository.findByGoalId(goalId);
+    }
+    
+    public List<Training> findTrainingsByAthletIdAndExerciseId(Long athletId, Long exerciseId) {
+        return trainingRepository.findByAthletIdAndExerciseId(athletId, exerciseId);
+    }
+    
+   
+    public void deleteTraining(Long id) {
+        trainingRepository.deleteById(id);
+    }
+    
+    public void deactivateTraining(Long id) {
+        trainingRepository.findById(id).ifPresent(training -> {
+            training.setIsActive(false);
+            trainingRepository.save(training);
+        });
+    }
+    
+    public void activateTraining(Long id) {
+        trainingRepository.findById(id).ifPresent(training -> {
+            training.setIsActive(true);
+            trainingRepository.save(training);
+        });
+    }
+    
+    public boolean trainingExists(Long athletId, Long exerciseId, Integer repetitionsOfSet, 
+                                 Integer repetitions, Double factorOfFmax) {
+        return trainingRepository
+                .findByAthletIdAndExerciseIdAndRepetitionsOfSetAndRepetitionsAndFactorOfFmax(
+                    athletId, exerciseId, repetitionsOfSet, repetitions, factorOfFmax)
+                .isPresent();
+    }
+    
+    public List<Training> findTrainingsCreatedBetween(LocalDateTime start, LocalDateTime end) {
+        return trainingRepository.findByCreatedBetween(start, end);
+    }
+    
+    public List<Training> findValidTrainingsAt(LocalDateTime date) {
+        return trainingRepository.findValidAt(date);
+    }
+
+       public void sendTrainingSummaryEmail(Long id) {
+               // TODO Auto-generated method stub
+               
+       }
+
+       public double calculateOneRepMax(double weight, int repetitions) {
+               // TODO Auto-generated method stub
+               return 0;
+       }
+}
\ No newline at end of file
diff --git a/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/util/AthletikConstants.java b/athletik-service/src/main/java/com/triathlon_coaching/product/athletik/backend/util/AthletikConstants.java
new file mode 100644 (file)
index 0000000..33a66c1
--- /dev/null
@@ -0,0 +1,7 @@
+package com.triathlon_coaching.product.athletik.backend.util;
+
+public class AthletikConstants {
+
+       public static final String NOREPLYEMAIL = "noreply@triathlon-coaching.com";
+
+}
diff --git a/athletik-service/src/main/resources/application.yaml b/athletik-service/src/main/resources/application.yaml
new file mode 100644 (file)
index 0000000..d963823
--- /dev/null
@@ -0,0 +1,55 @@
+
+spring:
+  datasource:
+    url: jdbc:postgresql://localhost:5432/sport
+#   username: laktatnebel
+#   password: njThR3G&c9?B7ZpQ
+    username: oleb
+    password: wpj_9+L6ukX+SN2-
+    driver-class-name: org.postgresql.Driver
+  jpa:
+    hibernate:
+      ddl-auto: validate
+    properties:
+      hibernate:
+        dialect: org.hibernate.dialect.PostgreSQLDialect
+        default_schema: athletik
+        show_sql: true
+        format_sql: true
+  mail:
+    host: localhost
+    port: 25
+    protocol: smtp
+    properties:
+      mail:
+        smtp:
+          auth: false
+          starttls:
+            enable: false
+  servlet:
+    multipart:
+      max-file-size: 10MB
+      max-request-size: 10MB
+  thymeleaf:
+    cache: false
+    prefix: classpath:/templates/
+    suffix: .html
+
+server:
+  port: 8080
+  servlet:
+    context-path: /athletik
+
+app:
+  base-url: http://localhost:8080
+
+logging:
+  level:
+    com.triathlon_coaching.product.athletik: DEBUG
+
+# Sicherheitskonfiguration
+security:
+  password:
+    encoder: sha512
\ No newline at end of file
diff --git a/athletik-service/src/main/webapp/training-summary.html b/athletik-service/src/main/webapp/training-summary.html
new file mode 100644 (file)
index 0000000..c3437e6
--- /dev/null
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8">
+    <title>Training Zusammenfassung</title>
+    <style>
+        body { font-family: Arial, sans-serif; line-height: 1.6; }
+        .header { background-color: #f8f9fa; padding: 20px; text-align: center; }
+        .content { padding: 20px; }
+        .exercise-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
+        .exercise-table th, .exercise-table td { border: 1px solid #ddd; padding: 8px; text-align: left; }
+        .exercise-table th { background-color: #f2f2f2; }
+        .summary { background-color: #e9ecef; padding: 15px; border-radius: 5px; }
+    </style>
+</head>
+<body>
+    <div class="header">
+        <h1>Dein Training vom <span th:text="${#temporals.format(trainingDate, 'dd.MM.yyyy')}"></span></h1>
+    </div>
+    
+    <div class="content">
+        <p>Aloha <span th:text="${athlet.fullname}"></span>,</p>
+        
+        <p>hier ist Deine Trainings-Zusammenfassung vom <span th:text="${#temporals.format(trainingDate, 'dd.MM.yyyy')}"></span>:</p>
+        
+        <table class="exercise-table">
+            <thead>
+                <tr>
+                    <th>Übung</th>
+                    <th>Sätze</th>
+                    <th>Wiederholungen</th>
+                    <th>Gewicht (kg)</th>
+                    <th>Volumen (kg)</th>
+                </tr>
+            </thead>
+            <tbody>
+                <tr th:each="exercise : ${trainingData}">
+                    <td th:text="${exercise.exercise.nameGerman}">Übungsname</td>
+                    <td th:text="${exercise.numberOfSet}">Sätze</td>
+                    <td th:text="${exercise.repetitions}">Wiederholungen</td>
+                    <td th:text="${#numbers.formatDecimal(exercise.weight, 1, 2)}">Gewicht</td>
+                    <td th:text="${#numbers.formatDecimal(exercise.weight * exercise.repetitions, 1, 2)}">Volumen</td>
+                </tr>
+            </tbody>
+        </table>
+        
+        <div class="summary">
+            <h3>Zusammenfassung</h3>
+            <p><strong>Gesamtvolumen:</strong> <span th:text="${#numbers.formatDecimal(totalVolume, 1, 2)}"></span> kg</p>
+            <p><strong>Anzahl Sätze:</strong> <span th:text="${totalSets}"></span></p>
+        </div>
+        
+        <p>Weiter so! Bleib dran für optimale Trainingserfolge.</p>
+        
+        <p>Mit sportlichen Grüßen<br>triathlon-coaching.com</p>
+    </div>
+</body>
+</html>
index b1518043e9eae5a2f753996a3e732a2f4d547b16..ad7bdfe2fbac1726a3a5c7ed9f43aacf4507c246 100644 (file)
@@ -1,18 +1,16 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
-       <groupId>com.triathlon-coaching.product</groupId>
-       <artifactId>athletik.athletik-web</artifactId>
-       <version>0.0.1-SNAPSHOT</version>
+       <artifactId>athletik.web</artifactId>
        <packaging>pom</packaging>
 
-       <name>${product.artifactId}</name>
+       <name>${project.artifactId}</name>
        <description>Athletik Training WebUI</description>
 
        <parent>
-               <groupId>de.laktatnebel.maven</groupId>
-               <artifactId>laktatnebelscript</artifactId>
-               <version>2.1.9</version>
+               <groupId>com.triathlon-coaching.product</groupId>
+               <artifactId>athletik</artifactId>
+               <version>0.0.1-SNAPSHOT</version>
        </parent>
 
 </project>
diff --git a/athletik-web/src/athletik_glob_vars.php b/athletik-web/src/athletik_glob_vars.php
new file mode 100644 (file)
index 0000000..f7c88f4
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+
+
+define ("HOST", "localhost");
+define ("SCHEMA", "athletik");
+define ("DB", "sport");
+//define ("USER", "laktatnebel");
+define ("USER", "oleb");
+//define ("PASS", "njThR3G&c9?B7ZpQ");
+define ("PASS", "wpj_9+L6ukX+SN2-");
+
+
+date_default_timezone_set('Europe/Berlin');
+
+?>
diff --git a/athletik-web/src/roadtokona_glob_vars.php b/athletik-web/src/roadtokona_glob_vars.php
deleted file mode 100644 (file)
index f7c88f4..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-
-define ("HOST", "localhost");
-define ("SCHEMA", "athletik");
-define ("DB", "sport");
-//define ("USER", "laktatnebel");
-define ("USER", "oleb");
-//define ("PASS", "njThR3G&c9?B7ZpQ");
-define ("PASS", "wpj_9+L6ukX+SN2-");
-
-
-date_default_timezone_set('Europe/Berlin');
-
-?>
diff --git a/athletik-web/src/webui/img/Big_Ben_London_closeup.jpg b/athletik-web/src/webui/img/Big_Ben_London_closeup.jpg
new file mode 100644 (file)
index 0000000..c494e47
Binary files /dev/null and b/athletik-web/src/webui/img/Big_Ben_London_closeup.jpg differ
diff --git a/athletik-web/src/webui/img/Reichstag_Berlin.jpg b/athletik-web/src/webui/img/Reichstag_Berlin.jpg
new file mode 100644 (file)
index 0000000..250d271
Binary files /dev/null and b/athletik-web/src/webui/img/Reichstag_Berlin.jpg differ
diff --git a/athletik-web/src/webui/js/athletik.js b/athletik-web/src/webui/js/athletik.js
new file mode 100644 (file)
index 0000000..ebcbebb
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * 
+ */
+
+document.addEventListener('DOMContentLoaded', function() {
+    const forms = document.querySelectorAll('.ajax-form');
+    
+    forms.forEach(form => {
+        form.addEventListener('submit', async function(e) {
+            e.preventDefault();
+            
+            const formData = new FormData(this);
+            const submitButton = this.querySelector('button[type="submit"]');
+            // Loading-State
+            submitButton.disabled = true;
+            submitButton.textContent = 'saving ...';
+            
+            try {
+                const response = await fetch('save-data.php', {
+                    method: 'POST',
+                    body: formData,
+                                       credentials: 'include', // Cookies mitsenden
+                                   headers: {
+                                       'X-Requested-With': 'XMLHttpRequest'
+                                   }
+                });
+                
+                const result = await response.json();
+                
+                if (result.success) {
+                    showMessage('Data saved successfully!', 'success');
+                    this.reset(); // Formular zurücksetzen
+                } else {
+                    showMessage('Error: ' + result.message, 'error');
+                }
+                
+            } catch (error) {
+                showMessage('Network error: ' + error.message, 'error');
+            } finally {
+                // Button zurücksetzen
+                submitButton.disabled = false;
+                submitButton.textContent = 'Save';
+            }
+        });
+    });
+    
+    function showMessage(text, type) {
+        const messageDiv = document.getElementById('responseMessage');
+        messageDiv.textContent = text;
+        messageDiv.className = type;
+        messageDiv.style.display = 'block';
+        
+        // Nachricht nach 5 Sekunden ausblenden
+        setTimeout(() => {
+            messageDiv.style.display = 'none';
+        }, 5000);
+    }
+});
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/database_functions.php b/athletik-web/src/webui/lib/database_functions.php
new file mode 100644 (file)
index 0000000..8f9d99e
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+require ("db/database_functions_pgsql.php");
+
+require ("db/database_functions_select.php");
+
+
+$selectFromViewMusclegroups = "SELECT * FROM ".SCHEMA.".view_musclegroups;";
+
+$selectWeightsFromViewTools = "SELECT * FROM ".SCHEMA.".view_toolweights WHERE id=%d;";
+
+$selectFromViewUsers = "SELECT name_user FROM ".SCHEMA.".view_users;";
+
+$selectUserNameFromViewUsers = "SELECT name_user FROM ".SCHEMA.".view_users WHERE id=%d;";
+
+$selectMucleGroupFromViewMuscles = "SELECT * FROM ".SCHEMA.".view_muscles WHERE fk_musclegroup=%d;";
+
+$selectMucleFromViewAllExercises = "SELECT * FROM ".SCHEMA.".view_allexercises WHERE id=%d;";
+
+$selectUserExerciseFromViewProtokoll = "SELECT * FROM ".SCHEMA.".view_protokollsetsoftoday WHERE fk_user=%d AND fk_exercise=%d;";
+
+//$selectUserExerciseFromViewProtokoll = "SELECT * FROM ".SCHEMA.".view_protokoll WHERE fk_user=%d AND fk_exercise=$d;";
+
+
+
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/db/database_functions_insert.php b/athletik-web/src/webui/lib/db/database_functions_insert.php
new file mode 100644 (file)
index 0000000..2dd4fa1
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+
+function generateInsertSchema($schema, $table, $values) {
+    $insert_str = "INSERT INTO ".$schema.".".$table." VALUES (";
+    
+    //print_r($values);
+    $anzValues = count($values);
+    for ($i = 0 ; $i < $anzValues; $i++) {
+        $insert_str .= $values[$i];
+        if ($i < ($anzValues-1)) {
+            $insert_str .= ", ";
+        }
+    }
+    
+    $insert_str .= ");";
+    //echo debugPrint($insert_str);
+    return $insert_str;
+}
+
+function generateInsertSchemaReturn($schema, $table, $values, $return) {
+    $insert_str = "INSERT INTO ".$schema.".".$table." VALUES (";
+    
+    //print_r($values);
+    $anzValues = count($values);
+    for ($i = 0 ; $i < $anzValues; $i++) {
+        $insert_str .= $values[$i];
+        if ($i < ($anzValues-1)) {
+            $insert_str .= ", ";
+        }
+    }
+    
+    $insert_str .= ") ".$return.";";
+    //echo debugPrint($insert_str);
+    return $insert_str;
+}
+
+function generateInsert($table, $values) {
+       $insert_str = "INSERT INTO ".$table." VALUES (";
+
+       //print_r($values);
+       $anzValues = count($values);
+       for ($i = 0 ; $i < $anzValues; $i++) {
+               $insert_str .= $values[$i];
+               if ($i < $anzValues-1) {
+                       $insert_str .= ", ";
+               }
+       }
+
+       $insert_str .= ");";
+       //echo debugPrint($insert_str);
+       return $insert_str;
+}
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/db/database_functions_pgsql.php b/athletik-web/src/webui/lib/db/database_functions_pgsql.php
new file mode 100644 (file)
index 0000000..7d3ca8e
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * Mit PostgresSQL verbinden */
+function getDBConnection() {
+
+       $str_connection = "host=".HOST." port=5432 dbname=".DB." user=".USER." password=".PASS." ";
+       //echo $str_connection;
+       // mit DB verbinden
+       //$connect_dbms_handle = new PDO("pgsql:dbname=".DB, USER, PASS);
+       $connect_dbms_handle = @pg_connect($str_connection) or die('Verbindungsaufbau fehlgeschlagen: ' . pg_last_error());
+
+       if ($connect_dbms_handle) {
+               return $connect_dbms_handle;
+       } else {
+               echo "Datenbankabfrage fehlgeschlagen!";
+               echo pg_last_error($connect_dbms_handle);
+               return false;
+       }
+}
+
+/**
+ * PostgresSQL-Verbindung schliessen */
+function closeDBConnection($ref_connection) {
+       @pg_close($ref_connection) or die('Verbindungsclose fehlgeschlagen: ');
+       //echo pg_last_error($ref_connection);
+}
+
+
+// SQL an DB absetzen
+// Parameter: Tablle, Feld(er), Wert(e)
+// Rückgabewert: boolean
+function getBooleanData ($dbms_connection, $db_query) {
+       // Variablen
+       $return_bool_value = false ;
+
+       // DB Abfage starten
+       //echo $db_query;
+       $res_sql_result = pg_query ($dbms_connection, $db_query);
+       // Gültigkeit der DB Abfage testen
+       if ($res_sql_result) {
+               //echo "<li>".$res_sql_result;
+               $return_bool_value = true;
+       } else {
+               return "Datenbankabfrage fehlgeschlagen!";
+       }
+
+       return $return_bool_value;
+}
+
+
+// SQL an DB absetzen
+// Parameter: Tablle, Feld(er), Wert(e)
+// Rückgabewert: Array
+function getData ($dbms_connection, $db_query) {
+    $return_arr_data = array();                 // Rückgabewert als Array
+    //echo "\n<p> SQL:<br>".$ref_str_db_query."</p>";
+    
+    // DB Abfage starten
+    //echo $db_query;
+    $res_sql_result = pg_query ($dbms_connection, $db_query);
+    // Gültigkeit der DB Abfage testen
+    if ($res_sql_result) {
+        //echo "<p>".$res_sql_result."</p>";
+        while ($arr_sql_data = pg_fetch_row($res_sql_result)) {
+            //echo debugPrint($arr_sql_data);
+            array_push ($return_arr_data, $arr_sql_data);
+        }
+        //var_dump( $return_arr_data);
+    } else {
+        return "Datenbankabfrage fehlgeschlagen!";
+    }
+    
+    return $return_arr_data;
+}
+
+
+// SQL an DB absetzen
+// Parameter: Tablle, Feld(er), Wert(e)
+// Rückgabewert: Array
+function getDataReturnID ($dbms_connection, $db_query) {
+    $return_arr_data = array();                 // Rückgabewert als Array
+    //echo "\n<p> SQL:<br>".$ref_str_db_query."</p>";
+    
+    // DB Abfage starten
+    //echo $db_query;
+    $res_sql_result = pg_query ($dbms_connection, $db_query);
+    // Gültigkeit der DB Abfage testen
+    if ($res_sql_result) {
+        //echo "<p>".$res_sql_result."</p>";
+        while ($arr_sql_data = pg_fetch_row($res_sql_result)) {
+            //echo debugPrint($arr_sql_data);
+            array_push ($return_arr_data, $arr_sql_data);
+        }
+        //var_dump( $return_arr_data);
+    } else {
+        return "Datenbankabfrage fehlgeschlagen!";
+    }
+    
+    return $return_arr_data[0][0];
+}
+
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/db/database_functions_select.php b/athletik-web/src/webui/lib/db/database_functions_select.php
new file mode 100644 (file)
index 0000000..fa8e18a
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+function generateSelect ($selectedFields, $table) {
+       $select_str = "SELECT ";
+       
+       if (is_array($selectedFields)) {
+               //print_r($selectedFields);
+       $anzFields = count($selectedFields);
+       for ($i = 0 ; $i < $anzFields; $i++) {
+               $select_str .= $selectedFields[$i];
+               if ($i < $anzFields-1) {
+                       $select_str .= ", ";
+               }
+       }
+       } else {
+           $select_str .= "*";
+       }
+       
+       $select_str .= " FROM ".$table;
+       
+       $select_str .= ";";
+
+       //echo $select_str;
+       return $select_str;
+}
+
+
+function generateSelectWhereOrder ($selectedFields, $table, $whereClause, $orderClause) {
+       $select_str = "SELECT ";
+
+       if (is_array($selectedFields)) {
+           //print_r($selectedFields);
+       $anzFields = count($selectedFields);
+       for ($i = 0 ; $i < $anzFields; $i++) {
+               $select_str .= $selectedFields[$i];
+               if ($i < $anzFields-1) {
+                       $select_str .= ", ";
+               }
+       }
+       } else {
+           $select_str .= "*";
+       }
+
+       $select_str .= " FROM ".$table;
+
+       if ($whereClause != null) {
+               $select_str .= " WHERE ";
+
+               //print_r($whereClause);
+               $anzWheres = count($whereClause);
+               for ($i = 0 ; $i < $anzWheres; $i++) {
+                       $select_str .= $whereClause[$i];
+                       if ($i < $anzWheres-1) {
+                               $select_str .= " AND ";
+                       }
+               }
+       }
+
+       if ($orderClause != null) {
+               $select_str .= " ORDER BY ";
+
+               //print_r($orderClause);
+               $anzOrders = count($orderClause);
+               for ($i = 0 ; $i < $anzOrders; $i++) {
+                       $select_str .= $orderClause[$i];
+                       if ($i < $anzOrders-1) {
+                               $select_str .= ", ";
+                       }
+               }
+       }
+
+       $select_str .= ";";
+
+       //echo $select_str;
+       return $select_str;
+}
+
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/gui/gui_functions_button.php b/athletik-web/src/webui/lib/gui/gui_functions_button.php
new file mode 100644 (file)
index 0000000..20ae213
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/** $ref_button_name                Name des <button>
+ *  $ref_value_button               Daten; null möglich
+ *  $ref_html_button                HTML zwischen <button></button>; null möglich
+ *  $ref_id                         Id des <button>
+ *  $ref_class                      Class des <button>; null möglich
+ *  $ref_tabindex                   Tabindex in der <form>
+ *  $ref_type                       <button>-type (submit, reset, button)
+ *  $ref_javascript                 Javascript; null möglich
+ *  $ref_labeltitle                 <label>-Text; null möglich
+ *  $ref_p_flag                     true, wenn alles in <p> soll
+ *  $ref_newline_flag               true, wenn <label> ueber <button> stehen soll
+ *  $ref_intend                     Anz. Tabs einrücken
+ * */
+function makeButton($ref_button_name, $ref_value_button, $ref_html_button, $ref_id, $ref_class, $ref_tabindex, $ref_type, $ref_javascript, $ref_labeltitle, $ref_p_flag, $ref_newline_flag, $ref_intend) {
+
+       $intend = make_intend_str($ref_intend);
+
+       if ($ref_p_flag) {
+               echo $intend;
+               echo "<p>\n";
+       }
+       
+
+       if ($ref_labeltitle != null) {
+               echo $intend."\t";
+               echo "<label for=\"".$ref_button_name."\">".$ref_labeltitle."</label>";
+               if ($ref_newline_flag) {
+                       echo "<br />";
+               }
+               echo "\n";
+       }
+
+       $str_id = $ref_id == null ? " " : " id=\"".$ref_id."\" ";
+       $str_class = $ref_class == null ? " " : " class=\"".$ref_class."\" ";
+       $str_tabindex = $ref_tabindex == null ? " " : " tabindex=\"".$ref_tabindex."\" ";
+       $str_javascript = $ref_javascript == null ? " " : " onchange=\"".$ref_javascript."\" ";
+
+       echo $intend."\t";
+
+       echo "<button ".$str_id.$str_class.$str_tabindex."name=\"".$ref_button_name."\" type=\"".$ref_type."\" ";
+       if ($ref_value_button != null) {
+               echo " value=\"".$ref_value_button."\"";
+       }
+       echo "/>\n";
+
+       echo $intend."\t\t";
+
+       echo $ref_html_button."\n";
+
+       echo $intend."\t";
+       echo "</button>\n";
+
+       if ($ref_p_flag) {
+               echo $intend;
+               echo "</p>\n";
+       }
+}
+
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/gui/gui_functions_input.php b/athletik-web/src/webui/lib/gui/gui_functions_input.php
new file mode 100644 (file)
index 0000000..0777c68
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+
+/** $ref_name                       Name des <input>
+ *  $ref_value_input                Daten; null möglich
+ *  $ref_id                         Id des <input>
+ *  $ref_class                      Class des <input>; null möglich
+ *  $ref_tabindex                   Tabindex in der <form>
+ *  $ref_type                       <input>-type (hidden, password, text ...)
+ *  $ref_size                       Größe des Feldes
+ *  $ref_maxlength                  Max. Größe der Eingabe
+ *  $ref_javascript                 Javascript; null möglich
+ *  $ref_mandantory                 Pflichtfeldmarkierung; null möglich
+ *  $ref_labeltitle                 <label>-Text; null möglich
+ *  $ref_p_flag                     true, wenn alles in <p> soll
+ *  $ref_newline_flag               true, wenn <label> ueber <input> stehen soll
+ *  $ref_intend                     Anz. Tabs einrücken
+ * */
+function makeInput($ref_name, $ref_value_input, $ref_id, $ref_class, $ref_tabindex, $ref_type, $ref_size, $ref_maxlength, $ref_javascript, $ref_mandantory, $ref_labeltitle, $ref_p_flag, $ref_newline_flag, $ref_intend) {
+
+       $intend = make_intend_str($ref_intend);
+
+       if ($ref_p_flag) {
+               echo $intend;
+               echo "<p>\n";
+       }
+       
+       echo $intend."\t";
+
+       if ($ref_mandantory) {
+               echo "* ";
+       }
+       
+       if ($ref_labeltitle != null) {
+               echo "<label for=\"".$ref_name."\">".$ref_labeltitle."</label>";
+               if ($ref_newline_flag) {
+                       echo "<br />";
+               }
+               echo "\n";
+       }
+
+       $str_id = $ref_id == null ? " " : " id=\"".$ref_id."\" ";
+       $str_class = $ref_class == null ? " " : " class=\"".$ref_class."\" ";
+       $str_tabindex = $ref_tabindex == null ? " " : " tabindex=\"".$ref_tabindex."\" ";
+       $str_javascript = $ref_javascript == null ? " " : " onchange=\"".$ref_javascript."\" ";
+
+       echo $intend."\t";
+
+       echo "<input ".$str_id.$str_class.$str_tabindex.$str_javascript."name=\"".$ref_name."\" type=\"".$ref_type."\" size=\"".$ref_size."\" maxlength=\"".$ref_maxlength."\" ";
+
+       if (isset($_POST[$ref_name]) && strlen($_POST[$ref_name]) > 0) {
+           echo " value=\"".$_POST[$ref_name]."\"";
+       } else if ($ref_value_input != null ){
+           echo " value=\"".$ref_value_input."\"";
+       }
+       echo "/>\n";
+
+       if ($ref_p_flag) {
+               echo $intend;
+               echo "</p>\n";
+       }
+}
+
+function make_multi_input($ref_name, $ref_type, $ref_size, $ref_maxlength, $ref_tabindex, $ref_id, $ref_labeltitle, $ref_class, $ref_classerrorextension, $ref_error_label, $ref_mandantory, $ref_intend, $ref_p_flag) {
+
+       if ($ref_p_flag) {
+               echo make_intend($ref_intend)."<p>\n";
+       }
+       echo make_intend($ref_intend)."\t";
+       if ($ref_mandantory) {
+               echo "*";
+       }
+       echo "<label for=\"".$ref_name[0]."\">".$ref_labeltitle."</label><br />\n";
+
+       for ($i=0; $i<count($ref_name); $i++) {
+               echo make_intend($ref_intend)."\t<input class=\"".$ref_class[$i];
+               if ($ref_error_label == $ref_name[$i]) {
+                       echo $ref_classerrorextension;
+               }
+               echo "\" type=\"".$ref_type[$i]."\" name=\"".$ref_name[$i]."\" size=\"".$ref_size[$i]."\" maxlength=\"".$ref_maxlength[$i]."\" tabindex=\"".$ref_tabindex[$i]."\" id=\"".$ref_id[$i]."\" ";
+               if (isset($_POST[$ref_name[$i]]) && strlen($_POST[$ref_name[$i]]) > 0) {
+                       echo " value=\"".$_POST[$ref_name[$i]]."\"";
+               }
+               echo "/>\n";
+       }
+       if ($ref_p_flag) {
+               echo make_intend($ref_intend)."</p>\n";
+       }
+}
+
+
+
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/gui/gui_functions_select.php b/athletik-web/src/webui/lib/gui/gui_functions_select.php
new file mode 100644 (file)
index 0000000..6a6c248
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+
+/** $ref_select_name                Name des <select>
+ *  $ref_value_array                Daten-Array; null möglich
+ *  $ref_id                         Id des <select>
+ *  $ref_class                      Class des <select>; null möglich
+ *  $ref_tabindex                   Tabindex in der <form>
+
+ *  $ref_start                      Start-Index statt Datenarray falls Datenarray null
+ *  $ref_end                        End-Index statt Datenarray falls Datenarray null
+ *  $ref_int_preselect              Index vorausgewähltes Element; default -1
+ *  $ref_multiple                   Mehrfachauswahl <select multiple>; default false
+ *  $ref_int_size                   Für mehrzeilige Mehrfachauswahl > 1;
+ *  $ref_javascript                 Javascript; null möglich
+ *  $ref_mandantory                 Pflichtfeldmarkierung; null möglich
+ *  $ref_labeltitle                 <label>-Text; null möglich
+ *  $ref_p_flag                     true, wenn alles in <p> soll
+ *  $ref_newline_flag               true, wenn <label> ueber <select> stehen soll
+ *  $ref_intend                     Anz. Tabs einrücken
+ * */
+function makeSelect($ref_select_name, $ref_value_array, $ref_id, $ref_class, $ref_tabindex, $ref_start, $ref_end, $ref_int_preselect, $ref_multiple, $ref_int_size, $ref_javascript, $ref_mandantory, $ref_labeltitle, $ref_p_flag, $ref_newline_flag, $ref_intend) {
+
+       $intend = make_intend_str($ref_intend);
+
+    if ($ref_p_flag) {
+        echo $intend;
+        echo "<p>\n";
+    }
+
+       echo $intend."\t";
+
+    if ($ref_mandantory) {
+        echo "* ";
+    }
+    
+       if ($ref_labeltitle != null) {
+               echo "<label for=\"".$ref_select_name."\">".$ref_labeltitle."</label>";
+               if ($ref_newline_flag) {
+                       echo "<br />";
+               }
+               echo "\n";
+       }
+
+       $str_id = $ref_id == null ? " " : " id=\"".$ref_id."\" ";
+       $str_class = $ref_class == null ? " " : " class=\"".$ref_class."\" ";
+       $str_tabindex = $ref_tabindex == null ? " " : " tabindex=\"".$ref_tabindex."\" ";
+       $str_javascript = $ref_javascript == null ? " " : " onchange=\"".$ref_javascript."\" ";
+
+       echo $intend."\t";
+
+       echo "<select ".$str_id.$str_class.$str_tabindex.$str_javascript."name=\"".$ref_select_name."\" size=\"".$ref_int_size."\" "." ";
+       if ($ref_multiple == true) {
+               echo "multiple=\"multiple\" ";
+       }
+       echo ">\n";
+
+       if ($ref_value_array != null) {
+
+               // Flag: True, falls das Datenarray multidimensional ist. Dann wird das Attribut value in <option> mit dem wert aus dem Unterarray gefüllt, ansonsten wird der Schleifenindex verwendet.
+               $multiDimensionalArrayFlag = is_array($ref_value_array[0]);
+
+               for ($i=0; $i<count($ref_value_array); $i++) {
+                       echo $intend;
+                       echo "\t\t<option value=\"".($multiDimensionalArrayFlag ? $ref_value_array[$i][0] : $i)."\"";
+                       if (($multiDimensionalArrayFlag ? $ref_value_array[$i][0] : ($i+1)) == $ref_int_preselect) { // $i-1 deshalb, weil das Array mit dem 0-ten Element beginnt, der Value aber mit 1 anfängt
+                               echo " selected";
+                       }
+                       echo ">".($multiDimensionalArrayFlag ? $ref_value_array[$i][1] : $ref_value_array[$i])."</option>\n";
+               }
+       } else {
+               for ($i=$ref_start; $i<(1+$ref_end); $i++) {
+                       echo $intend;
+                       echo "\t\t<option value=\"".$i."\"";
+                       if ($i == $ref_int_preselect) {
+                               echo " selected";
+                       }
+                       echo ">".$i."</option>\n";
+               }
+       }
+
+       echo $intend."\t";
+       echo "</select>\n";
+
+       if ($ref_p_flag) {
+               echo $intend;
+               echo "</p>\n";
+       }
+}
+
+
+
+
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/gui_functions.php b/athletik-web/src/webui/lib/gui_functions.php
new file mode 100644 (file)
index 0000000..89e981f
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+require ("gui/gui_functions_select.php");
+require ("gui/gui_functions_input.php");
+require ("gui/gui_functions_button.php");
+
+
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/session/session_functions.php b/athletik-web/src/webui/lib/session/session_functions.php
new file mode 100644 (file)
index 0000000..9d2ae61
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+function sessionHandling($scriptname, $dbms_connection) {
+       session_start();
+
+       $sid = session_id();
+
+       if (isset($_POST['username']) && isset($_POST['pass'])) {
+               // eigentliche Prüfung
+               if (checkLogin($dbms_connection, $_POST['username'],$_POST['pass']) == 1) {
+                       $_SESSION['sess_user'] = $_POST['username'];
+               } else {
+                       $_SESSION['sess_user'] = "guest";
+               }
+       } else if (!isset($_SESSION['sess_user']) || $_POST['logout'] == "Abmelden") {
+               $_SESSION['sess_user'] = "guest";
+       }
+
+       $session_user = "0";
+
+       if ($_SESSION['sess_user'] == "guest") {
+               $session_user = "0";
+       } else {
+               $session_user = "(SELECT id FROM users WHERE username='".$_SESSION['sess_user']."')";
+       }
+
+       getData($dbms_connection, generateInsert(SCHEMA, "session", array("'".$sid."'", $session_user, "now()", "'".$page."'")));
+
+
+
+}
+
+function checkLogin($dbms_connection, $ref_user, $ref_pass) {
+    
+    
+    $str = "SELECT count(*) FROM users WHERE username='".$ref_user."' AND pass='".$ref_pass."'";
+    
+}
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/session_functions.php b/athletik-web/src/webui/lib/session_functions.php
new file mode 100644 (file)
index 0000000..90ea70a
--- /dev/null
@@ -0,0 +1,5 @@
+<?php
+
+require ("session/session_functions.php");
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/util/util_functions.php b/athletik-web/src/webui/lib/util/util_functions.php
new file mode 100644 (file)
index 0000000..f0e70c7
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+function debugPrint($param) {
+       echo "<p>";
+       if (is_array($param)) {
+               if ($asList) {
+                   debugPrintAsList($param);
+               } else {
+                       echo "_";
+                       foreach ($param as $p) {
+                               echo $p."_";
+                       }
+               }
+       } else {
+               echo $param;
+       }
+       echo "</p>\n";
+}
+
+function debugPrintAsList($param) {
+       echo "<ol>\n";
+       foreach ($param as $p) {
+               echo "<li>".$p."_</li>\n";
+       }
+       echo "</ol>\n";
+}
+
+function coordinates ($ref_longitude, $ref_latitude) {
+       $return_geo = array("","");
+
+       $return_geo[0] = $ref_longitude[0] + $ref_longitude[1]/60 + $ref_longitude[2]/3600;
+       $return_geo[1] = $ref_latitude[0] + $ref_latitude[1]/60 + $ref_latitude[2]/3600;
+
+       return $return_geo;
+}
+
+function find_pos_in_array($ref_array, $ref_value) {
+       $pos = -1;
+       
+       for ($i=0; $i<count($ref_array); $i++) {
+               if ($ref_array[$i] == $ref_value) {
+                       $pos = $i;
+               }
+       }
+       
+       return $pos;
+}
+
+function make_intend($ref_count) {
+       for ($i=0; $i<$ref_count; $i++) {
+               echo "\t";
+       }
+}
+
+function make_intend_str($ref_count) {
+       $intend = "";
+       for ($i=0; $i<$ref_count; $i++) {
+               $intend .= "\t";
+       }
+       return $intend;
+}
+
+
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/lib/util_functions.php b/athletik-web/src/webui/lib/util_functions.php
new file mode 100644 (file)
index 0000000..5ef078c
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+require ("util/util_functions.php");
+
+
+function getArrayOfWeights($connect_dbms_handle, $selectWeightsFromViewTools, $ref_tool) {
+    
+    $dataWeights = getData($connect_dbms_handle, sprintf($selectWeightsFromViewTools, $ref_tool));
+    //print_r($dataWeights);
+    $weightsArray = array();
+    
+    for ($weight = $dataWeights[0][1]; $weight <= $dataWeights[0][3]; $weight += $dataWeights[0][2]) {
+        //echo $weight;
+        array_push($weightsArray, array ($weight, $weight." kg"));
+    }
+    
+    //print_r($weightsArray);
+    
+    return $weightsArray;
+}
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/save-data.php b/athletik-web/src/webui/save-data.php
new file mode 100644 (file)
index 0000000..6cb2a1c
--- /dev/null
@@ -0,0 +1,84 @@
+<?php require_once '../athletik_glob_vars.php'; ?>
+<?php require_once 'lib/database_functions.php'; ?>
+<?php require_once 'lib/session_functions.php'; ?>
+<?php
+session_start();
+
+header('Content-Type: application/json');
+
+header('Access-Control-Allow-Origin: https://www.triathlon-coaching.com');
+header('Access-Control-Allow-Credentials: true');
+header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
+header('Access-Control-Allow-Headers: Content-Type');
+// Session Cookie Einstellungen (in deiner Haupt-Login-Datei)
+session_set_cookie_params([
+'lifetime' => 3600,
+'path' => '/',
+'domain' => '.triathlon-coaching.com',
+'secure' => true,
+'httponly' => true,
+'samesite' => 'Lax'
+]);
+
+error_log ("hallo", 0);
+
+// AJAX Request check
+function is_ajax_request() {
+    return isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
+    strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
+}
+
+if (!is_ajax_request() || $_SERVER['REQUEST_METHOD'] !== 'POST') {
+    http_response_code(403);
+    echo json_encode(['success' => false, 'message' => 'Forbidden']);
+    exit;
+}
+
+error_log ("hallo2", 0);
+
+// Login checsessionk
+if (!isset($_SESSION['user_id']) || !isset($_SESSION['logged_in'])) {
+    echo json_encode(['success' => false, 'message' => 'Please login again']);
+    exit;
+}
+
+$current_user = $_SESSION['user_id'];
+
+error_log ("hallo".$current_user, 0);
+
+
+if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
+    http_response_code(405);
+    echo json_encode(['success' => false, 'message' => 'Method not allowed']);
+    exit;
+}
+
+foreach (array_keys($_POST) as $post) {
+    //echo $post." => ".$_POST[$post]."\n";
+    $change = explode ("_", $post);
+    $exercise += $change[count($change)-1];
+    
+    //echo $exercise;
+}
+
+$exercise = $exercise / count(array_keys($_POST));
+
+$update_arr = array();
+array_push($update_arr, $current_user);
+array_push($update_arr, $exercise);
+array_push($update_arr, "now()");
+array_push($update_arr, $_POST['set_'.$exercise]);
+array_push($update_arr, $_POST['reps_'.$exercise]);
+array_push($update_arr, $_POST['weight_'.$exercise]);
+
+$connect_dbms_handle = getDBConnection();
+
+$sqlInsert = generateInsertSchema(SCHEMA, "protokoll", $update_arr);
+$sqlInsert_result = getBooleanData($connect_dbms_handle, $sqlInsert);
+//echo $sqlinsert_result;
+
+$sqllog = generateInsertSchema("ticket", "dt_sqllog", array("DEFAULT", "now()", "'".addslashes(str_replace("'", "\"", $sqlupdate))."'", $current_user, "'".uniqid()."'", "'".$page."'"));
+$sqllog_result = getBooleanData($connect_dbms_handle, $sqllog);
+
+
+?>
\ No newline at end of file
diff --git a/athletik-web/src/webui/stylesheet/athletik.css b/athletik-web/src/webui/stylesheet/athletik.css
new file mode 100644 (file)
index 0000000..07a8a39
--- /dev/null
@@ -0,0 +1,124 @@
+@charset "UTF-8";
+
+BODY {
+       font-family: sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
+               'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
+               'Helvetica Neue';
+       line-height: 1.5;
+       font-size: 1rem;
+       margin: 0;
+}
+
+HEADER, SECTION {
+       margin-left: 15em;
+       padding: 0 0 0 1rem;
+}
+
+NAV {
+       background: #f0f0f0;
+       height: 100%;
+       position: fixed;
+       z-index: 1;
+       top: 0;
+       left: 0;
+       width: 15em;
+       overflow-x: hidden;
+}
+
+OL {
+       margin: 1.25em   0;
+}
+
+UL.navbar {
+       list-style-type: none;
+}
+
+.navbar li a {
+       text-decoration: none;
+       color: #333;
+       padding: 0.5rem 1rem;
+       display: block;
+       font-size: 2em;
+}
+
+.navbar li a:active, .navbar li a:hover {
+       background: #ddd;
+       transition: background 0.2s ease;
+}
+
+P.muscle {
+       border-top: 0.2em solid black;
+       padding: 0.25em 0 0 0;
+}
+
+P.start {
+       font-size: 1.25rem;
+       padding: 0 0 1.5em 0;
+}
+
+LI, P {
+       font-size: 1rem;
+}
+
+A {
+       text-decoration: none;
+}
+
+.musclename {
+       font-weight: 600;
+       color: #933;
+       background-color: #ddd;
+       padding: 0.5em;
+}
+
+.important {
+       font-weight: 600;
+       color: red;
+}
+
+.fa {
+       font-size: 1.5em;
+}
+
+H1 {
+       text-align: center;
+       font-size: 4.5em;
+}
+
+H2 {
+       text-align: center;
+       font-size: 3em;
+       text-decoration: underline overline;
+}
+
+.totop {
+       background-color: #666;
+       color: #ccc;
+       padding: 0.5em 2em;
+}
+
+.totop A {
+       color: #ccc;
+       font-size: 0.75em;
+}
+
+.hidden { display: none; }
+.success { 
+    background: #d4edda; 
+    color: #155724; 
+    padding: 10px; 
+    margin: 10px 0; 
+    border: 1px solid #c3e6cb;
+}
+.error { 
+    background: #f8d7da; 
+    color: #721c24; 
+    padding: 10px; 
+    margin: 10px 0; 
+    border: 1px solid #f5c6cb;
+}
+
+button:disabled {
+    opacity: 0.6;
+    cursor: not-allowed;
+}
\ No newline at end of file
diff --git a/athletik-web/src/webui/training.php b/athletik-web/src/webui/training.php
new file mode 100644 (file)
index 0000000..1207a15
--- /dev/null
@@ -0,0 +1,1114 @@
+<?php
+putenv("PGGSSENCMODE=disable");
+putenv("LC_ALL=C");
+?>
+<?php require_once '../athletik_glob_vars.php'; ?>
+<?php require_once 'lib/database_functions.php'; ?>
+<?php require_once 'lib/session_functions.php'; ?>
+<?php require_once 'lib/gui_functions.php'; ?>
+<?php require_once 'lib/util_functions.php'; ?>
+<?php
+$page = 'index.php';
+
+$j=0;
+
+$connect_dbms_handle = getDBConnection();
+
+$current_user=1;
+$lang=2;
+
+$dataUsername = getData($connect_dbms_handle, sprintf($selectUserNameFromViewUsers, $current_user));
+
+
+
+
+?>
+<!DOCTYPE html>
+<html lang="de">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="stylesheet"
+       href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
+<title>Athletiktraining <?php echo $dataUsername[0][0]; ?></title>
+<link rel="stylesheet" href="stylesheet/athletik.css" type="text/css" media="screen, projection" />
+
+</head>
+<body>
+
+       <header>
+               <h1 id="nav"><span class="eng">Exercises</span> / <span class="deu">Übungen</span></h1>
+       </header>
+
+       <nav>
+           <img src="img/Reichstag_Berlin.jpg" alt="deutsch" height="50px"><img src="img/Big_Ben_London_closeup.jpg" alt="english" height="50px">
+               <ul class="navbar">
+                       <li><a href="#warmup">Aufwärmen</a></li>
+                       <li><a href="#general">Allgemeine Hinweise</a></li>
+                       <li><a href="#combinations">Sinnvolle Sessions</a></li>
+<?php
+$dataMusclegroups = getData($connect_dbms_handle, $selectFromViewMusclegroups);
+    
+foreach ($dataMusclegroups as $musclegroup) {
+?>
+                       <li><a href="#mg<?php echo $musclegroup[0]; ?>"><span class="eng"><?php echo $musclegroup[1] ?></span> / <span class="deu"><?php echo $musclegroup[2] ?></span></a></li>";
+<?php
+}
+?>
+<!-- 
+                       <li><a href="#arms">Arme</a></li>
+                       <li><a href="#shoulders">Schulter</a></li>
+                       <li><a href="#chest">Brust</a></li>
+                       <li><a href="#back">Rücken</a></li>
+                       <li><a href="#lowerbody">Rumpf</a></li>
+                       <li><a href="#hips">Hüfte & Gesäß</a></li>
+                       <li><a href="#legs">Beine</a></li>
+ -->
+                       <li><a href="#cardio">Cardio</a></li>
+                       <li><a href="#stretching">Stretching</a></li>
+               </ul>
+       </nav>
+
+       <section id="warmup">
+               <h2>Warmup</h2>
+               <p class="start">Geräte: Crosstrainer oder Rudergerät</p>
+               <ol>
+                       <li>Kreislauf "starten": 10 min mindestens</li>
+                       <li>Mobilisierung: Alle Gelenke durchbewegen:
+                               <ul>
+                                       <li>Handgelenke kreisen</li>
+                                       <li>Armkreisen</li>
+                                       <li>Kopf/Hals</li>
+                                       <li>Oberkörper vor- und zurückbeugen</li>
+                                       <li>Hüftkreisen</li>
+                                       <li>2-3 mal Anfersen</li>
+                                       <li>Füße kreisen lassen</li>
+                               </ul>
+                       </li>
+               </ol>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="general">
+               <h2>Allgemeine Hinweise</h2>
+               <ul>
+                       <li>alles außer Rumpf:
+                               <ul><li>
+                       75% der Maximalkraft
+                       </li><li>Bewegung: Explosiv ausführen, 2 Sekunden zurückführen, 1-2
+                               Sekunden halten</li>
+                       <li>3 Sätze a 10 Wiederholungen</li>
+               </ul>
+               </li>
+               <li>Rumpf:
+                       <ul>
+                               <li>50% der Maximalkraft</li>
+                               <li>Bewegung: zügig in beide Richtungen ausführen, ohne halten</li>
+                               <li>3 Sätze a 20-30 Wiederholungen</li>
+                       </ul>
+               </li>
+               <li>Ca. 1 Min. Erholung zwischen den Sätzen.</li>
+               <li>Wenn die Bewegungsqualität unsauber wird: Abbruch der Übung!</li>
+               <li><span class="important">Scheiben auf der Langhantel
+                               immer mit Klammern sichern !!!</span></li>
+               <li>Begriffe:<br />Barbell: Langhantel<br />Bumbbell:
+                       Kurzhantel
+               </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="combinations">
+               <h2>Sinnvolle Sessions</h2>
+               <ul>
+                       <li><ol>
+                       <li><a href="#warmup">Aufwärmen</a></li>
+                       <li><a href="#arms">Arme</a></li>
+                       <li><a href="#shoulders">Schulter</a></li>
+                       <li><a href="#stretching">Stretching</a></li>
+                       </ol></li>
+                       <li><ol>
+                       <li><a href="#warmup">Aufwärmen</a></li>
+                       <li><a href="#chest">Brust</a></li>
+                       <li><a href="#back">Rücken</a></li>
+                       <li><a href="#lowerbody">Rumpf</a></li>
+                       <li><a href="#stretching">Stretching</a></li>
+                       </ol></li>
+                       <li><ol>
+                       <li><a href="#warmup">Aufwärmen</a></li>
+                       <li><a href="#hips">Hüfte & Gesäß</a></li>
+                       <li><a href="#legs">Beine</a></li>
+                       <li><a href="#stretching">Stretching</a></li>
+                       </ol></li>
+                       <li><ol>
+                       <li><a href="#warmup">Aufwärmen</a>, wenn Cardio im Gym</li>
+                       <li><a href="#cardio">Cardio</a></li>
+                       <li><a href="#stretching">Stretching</a></li>
+                       </ol></li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+<?php
+foreach ($dataMusclegroups as $musclegroup) {
+?>
+    <section id="mg<?php echo $musclegroup[0] ?>">
+               <h2><span class="eng"><?php echo $musclegroup[1] ?></span> / <span class="deu"><?php echo $musclegroup[2] ?></span></h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+<?php          
+        $dataMuscles = getData($connect_dbms_handle, sprintf($selectMucleGroupFromViewMuscles, $musclegroup[0]));
+        foreach ($dataMuscles as $muscle) {
+?>
+               <p class="muscle">
+                       Muskel: <span class="musclename"><?php echo $muscle[1] ?> / <span class="eng"><?php echo $muscle[2] ?></span> / <span class="deu"><?php echo $muscle[3] ?></span></span><br /> Bewegung:
+                       <span class="eng"><?php echo $muscle[4] ?></span> / <span class="deu"><?php echo $muscle[5] ?></span>
+               </p>
+               <ul>
+<?php          
+            $dataAllExercises = getData($connect_dbms_handle, sprintf($selectMucleFromViewAllExercises, $muscle[0]));
+
+            foreach ($dataAllExercises as $exercise) {
+?>
+                       <li>
+                               <p>
+                                       Gerät: <span class="eng"><?php echo $exercise[7] ?></span> / <span class="deu"><?php echo $exercise[8] ?></span><br />
+                                       <a target="_blank" href="><?php echo $exercise[5] ?>">
+                                               <span class="eng"><?php echo $exercise[17] ?></span> / <span class="deu"><?php echo $exercise[3] ?></span><span class="fa">&#xf08e;</span>
+                                       </a><br />
+                                       <form name="protokoll_"<?php echo $exercise[0] ?> id="protokoll_"<?php echo $exercise[0] ?> class="ajax-form">
+<?php
+                $dataProtokoll = getData($connect_dbms_handle, sprintf($selectUserExerciseFromViewProtokoll, $current_user, $exercise[0]));
+                print_r($dataProtokoll);
+                
+                $nextSet = count($dataProtokoll) + 1;
+                $weightarray = getArrayOfWeights($connect_dbms_handle, $selectWeightsFromViewTools, $exercise[6]);
+                
+                makeInput("set_".$exercise[0], $nextSet, "set_".$exercise[0], null, null, "text", 1, 1, null, null, "Set: ", false, false, 6 );
+                makeSelect("reps_".$exercise[0], null, "rep_".$exercise[0], null, null, 1, 50, -1, false, 1, null, null, "Reps.: ", false, false, 6);
+                makeSelect("weight_".$exercise[0], $weightarray, "weight_".$exercise[0], null, null, 0, 0, -1, false, 1, null, null, null, false, false, 6);
+                makeButton("save_".$exercise[0], "save_".$exercise[0], "Save", "save_".$exercise[0], null, null, "submit", null, null, false, false, 6);
+  
+
+?>
+                                       </form><div id="responseMessage_"<?php echo $exercise[0] ?> class="hidden"></div>
+                               </p>
+                       </li>
+
+<?php
+            }
+?>
+               </ul>
+<?php
+               }
+?>
+       </section>
+       
+<?php
+}
+?>
+       <section id="arms">
+               <h2>Arme</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Triceps Brachii / Trizeps</span><br /> Bewegung:
+                       Armstreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Triceps/DBLyingTriExt">Dumbbell
+                                               Lying Triceps Extension <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Triceps/CBPushdown">Cable
+                                               Pushdown <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Biceps Brachii / Bizeps</span><br /> Bewegung:
+                       Armbeugung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Easy Bar Langhantel (die geschwungene Langhantel)<br /> <a
+                                               target="_blank"
+                                               href="https://exrx.net/WeightExercises/Biceps/BBCurl">Barbell
+                                               Curl <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Biceps/DBCurl">Dumbbell
+                                               Curl <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Biceps/CBCurl">Cable
+                                               Curl <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Brachialis</span><br /> Bewegung:
+                       Armbeugung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Brachialis/DBPreacherCurl">Dumbbell
+                                               Preacher Curl <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="shoulders">
+               <h2>Schulter</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Anterior Deltoid / Deltamuskel vorne</span><br />
+                       Bewegung: Oberarm anheben
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/DeltoidAnterior/DBShoulderPress">Dumbbell
+                                               Shoulder Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Posterior Deltoid / Deltamuskel hinten</span><br />
+                       Bewegung: Oberarm nach hinten ziehen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug (ggf. 2)<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/DeltoidPosterior/CBStandingReverseFly">Cable
+                                               Reverse Fly <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Lateral Deltoid / Deltamuskel seitlich</span><br />
+                       Bewegung: Oberarm anheben
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Langhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/DeltoidLateral/BBUprightRow">Barbell
+                                               Upright Row <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Supraspinatus</span><br />
+                       Bewegung: Oberarm schräg anheben
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Supraspinatus/DBFrontLateralRaise">Dumbbell
+                                               Front Lateral Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="chest">
+               <h2>Brust</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Pectoralis Major/ Brustmuskel groß, Ansatz am
+                               Brustbein</span><br /> Bewegung: Schulterstreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralSternal/DBBenchPress">Dumbbell
+                                               Bench Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Langhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralSternal/BBBenchPress">Barbell
+                                               Bench Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Pectoralis Major / Brustmuskel groß, Ansatz am
+                               Schlüsselbein</span><br /> Bewegung: Schulterstreckung <br /> <span
+                               class="important">Rückenlage auf einer schrägen (!) Bank</span>
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralClavicular/DBInclineBenchPress">Dumbbell
+                                               Incline Bench Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Langhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralClavicular/BBInclineBenchPress">Barbell
+                                               Incline Bench Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralClavicular/CBInclineFly">Cable
+                                               Incline Fly <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Pectoralis Minor / Brustmuskel klein</span><br />
+                       Bewegung: Schulterstreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralSternal/CBStandingFly">Cable
+                                               Standing Fly <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Serratus Anterior / Sägemuskel</span><br /> Bewegung:
+                       Schulterstreckung <br /> <span class="important">Rückenlage
+                               auf einer schrägen (!) Bank</span>
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Langhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/SerratusAnterior/BBInclineShoulderRaise">Barbell
+                                               Incline Shoulder Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/SerratusAnterior/DBInclineShoulderRaise">Dumbbell
+                                               Incline Shoulder Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="back">
+               <h2>Rücken</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Latissimus Dorsi</span><br /> Bewegung:
+                       Oberarm anlegen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Lat-Zug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/LatissimusDorsi/CBFrontPulldown">Cable
+                                               Pulldown <span class="fa">&#xf08e;</span>
+                                       </a> <br /> <span class="important">Keine Rückenlage oder mit
+                                               dem Körpergewicht arbeiten</span>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Trapezius / Trapezmuskel</span><br /> Bewegung:
+                       Schulter anheben
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/TrapeziusUpper/DBShrug">Dumbbell
+                                               Shrug <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Rücken</span><br /> Bewegung:
+                       Schultern zusammenziehen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Rudermaschine / Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/BackGeneral/CBStraightBackSeatedRow">Cable
+                                               Straight Back Seated Row <span class="fa">&#xf08e;</span> <br />
+                                               <span class="important">Keine Rückenlage oder mit dem
+                                                       Körpergewicht arbeiten; Rücken bleibt gerade</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/BackGeneral/DBBentOverRow">Dumbbell
+                                               Bent-over Row <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Infraspinatus</span><br />
+                       Bewegung: Außenrotation
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Infraspinatus/CBStandingExternalRotation">Cable
+                                               Standing Shoulder External Rotation <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Subscapularis</span><br />
+                       Bewegung: Innenrotation
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Subscapularis/CBStandingInternalRotation">Cable
+                                               Standing Shoulder Internal Rotation <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="lowerbody">
+               <h2>Rumpf</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Rectus Abdominis / gerade Bauchmuskeln</span><br />
+                       Bewegung: Rumpfbeugung<br /> <span class="important">Tip: Übungen für schräge und gerade Bauchmuskeln nicht direkt hintereinander</span>
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/RectusAbdominis/BWSitUpAD">Sit-up <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Ball<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/RectusAbdominis/BWBallCrunch">Ball Crunch (on stability ball) <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: TRX<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/RectusAbdominis/STJackknife">Suspended Jack-knife <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: schräge Bank<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/RectusAbdominis/BWInclineSitUp">Incline Sit-up <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Obliques / schräge Bauchmuskeln</span><br />
+                       Bewegung: Rumpfbeugung schräg<br /> <span class="important">Tip: Übungen für schräge und gerade Bauchmuskeln nicht direkt hintereinander</span>
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Bank<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Obliques/BWTwistingCrunch">Twisting Crunch <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Ball<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Obliques/BWTwistingBallCrunch">Twisting Crunch (on stability ball) <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: TRX<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Obliques/STTwistingJackknife">Suspended Twisting Jack-knife <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: schräge Bank<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Obliques/BWInclineTwistingSitUp">Incline Twisting Sit-up <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Erector Spinae / Rückenmuskel</span><br />
+                       Bewegung: Rumpfstreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/ErectorSpinae/BWAlternatingBirdDog">Alternating Bird Dog <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/ErectorSpinae/Superman">Superman <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Ball<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/ErectorSpinae/BWHyperextensionBall">Back Extension (on stability ball) <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/ErectorSpinae/DBOneArmStraightLegDeadlift">Dumbbell One Arm Straight Leg Deadlift <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: schräge Bank<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/ErectorSpinae/BW45HyperextensionHips">45° Hyperextension <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="hips">
+               <h2>Hüfte & Gesäß</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Gluteus Maximus / großer Gesäßmuskel</span><br />
+                       Bewegung: Hüftstreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/GluteusMaximus/BWSquat">Squat <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/GluteusMaximus/BWLunge">Lunges <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/GluteusMaximus/DBSplitSquat">Dumbbell Split Squat <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/GluteusMaximus/DBFrontSquat">Dumbbell Front Squat <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Gluteus Minor u.a. / kleiner Gesäßmuskel</span><br />
+                       Bewegung: Bein Abspreizen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/HipAbductor/BWSideBridgeHipAbduction">Side Bridge Hip Abduction <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/HipAbductor/CBHipAbduction">Cable Hip Abduction <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Piriformes u.a.</span><br />
+                       Bewegung: Beine schließen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/HipAdductors/CBHipAdduction">Cable Hip Adduction <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Rectus Femoris u.a. / Hüftbeuger</span><br />
+                       Bewegung: Hüftbeugung<br /><span class="important">Tip: Geht auch mit gestreckten Beinen</span>
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/HipFlexors/BWLyingLegRaise">Lying Leg Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/HipFlexors/CBLyingLegRaiseBench">Cable Lying Leg Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="legs">
+               <h2>Beine</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Quadrizeps femoris / Oberschenkel</span><br />
+                       Bewegung: Kniestreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Beinpresse<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Quadriceps/SLHackSquat">Hack Squat <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Bizeps femoris u.a. / Unterschenkel</span><br />
+                       Bewegung: Kniebeugung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Hamstrings/CBLyingLegCurl">Cable Lying Leg Curl <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Gastrocnemius / zweibäuchiger Wadenmuskel</span><br />
+                       Bewegung: Fuß strecken / Knie beugen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Beinpresse<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Gastrocnemius/SLLyingCalfPress">Sled Lying Calf Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Gastrocnemius/DBSingleLegCalfRaise">Dumbbell Single Leg Calf Raise
+ <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Soleus / Schollenmuskel</span><br />
+                       Bewegung: Fuß strecken
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Multipresse<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Soleus/SMSeatedCalfRaise">Smith Seated Calf Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Tibialis Anterior / vorderer Schienbeinmuskel</span><br />
+                       Bewegung: Fuß anheben
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/TibialisAnterior/DBReverseCalfRaise">Dumbbell Reverse Calf Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Beinpresse<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/TibialisAnterior/SLHackReverseCalfRaise">Sled Hack Reverse Calf Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="cardio">
+               <h2>Cardio</h2>
+               <p class="muscle">
+                       Laufen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Dauerläufe in ruhigem Tempo, so daß Du noch durch die Nase atmen kannst.<br />Dauer: 30-60 min
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Intervalläufe, 5-10 x 300-500m schnell und hart, 2-4 min Trab- / Gehpause.<br />Vorher 15-20 min einlaufen, anschließend 10 min auslaufen. Immer.
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Berganläufe, 5-10 x 1-2 min schnell und hart, 2-4 min Trab- / Gehpause.<br />Vorher 15-20 min einlaufen, anschließend 10 min auslaufen. Immer.
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Schwimmen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Dauerschwimmen, 1000 bis ?? m in ruhigem Tempo in verschiedenen Lagen.
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       <a href="https://swim.triathlon-coaching.com">Schwimmtrainingspläne</a> (fast) ohne Ende...
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Zirkeltraining / CrossFit<br />Jede Übung 1 min, 1min Pause
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kettleball<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Kettlebell/KBTwoArmSwing">Kettlebell Two Arm Swing <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/Aerobic/Exercises/Burpee">Burpee <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Medizinball<br /> <a target="_blank"
+                                               href="https://exrx.net/Plyometrics/MBWallShot">Medicine Ball Wall Shot <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Box / Step<br /> <a target="_blank"
+                                               href="https://exrx.net/Plyometrics/BoxLateralJump">Lateral Box Jumps <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Medizinball<br /> <a target="_blank"
+                                               href="https://exrx.net/Plyometrics/MBChestThrowSitupWall">Medicine Ball Chest Throw Sit-up (on wall) <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Tau<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Power/RopeWave">Rope Wave <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Power/DBThruster">Dumbbell Thruster <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/Plyometrics/SplitJump">Split Jump <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="stretching">
+               <h2>Stretching</h2>
+               <p class="start">Jede Übung 3 mal (pro Seite)<br />Anpannen - Entspannen - Dehnen<br />Pflicht: Die Muskeln, die man auch trainiert hat, dehenen</p>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Hals</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Sternocleidomastoid/NeckRetraction">Neck Retraction Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Splenius/Neck">Neck Extensor Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Schulter</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/DeltoidAnterior/Doorway">Doorway Front Deltoid Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/DeltoidLateral/SideDelt">Side Deltoid Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/DeltoidPosterior/RearDelt">Rear Deltoid Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Arme</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Triceps/Overhead">Overhead Triceps Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Biceps/Seated">Seated Biceps Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Brachioradialis/Standing">Standing Brachioradialis Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/WristFlexors/Seated">Seated Wrist Flexor Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/WristExtensors/Single">Single Arm Wrist Extensor <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Rücken</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/BackGeneral/PCCrossHand">Lever Back Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/LatissimusDorsi/Overhead">Overhead Lat Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/LatissimusDorsi/StandingSideReach">Standing Side Reach Lat Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Rhomboids/Hugging">Hugging Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Infraspinatus/SideLying">Side Lying Infraspinatus <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Subscapularis/Doorway">Doorway Subscapularis Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Brust</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/ChestGeneral/StraightArm">Straight Arm Chest Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/PectoralisMinor/Wall">Wall Angel <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Bauch</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/RectusAbdominis/Prone">Lying (prone) Abdominal Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Obliques/LyingCrossover">Lying Crossover Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Obliques/Pretzel">Pretzel Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/ErectorSpinae/SeatedFloor">Seated Lower Back Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Hüfte</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/GluteusMaximus/LyingModified">Lying Glute Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/GluteusMaximus/Squatting">Squatting Glute Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAbductors/WallIliotibial">Wall Iliotibial Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAbductors/Pretzel">Seated Pretzel Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAbductors/SeatedHipInternalRotator">Seated Hip Internal Rotator Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAbductors/LyingCrossover">Lying Crossover Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipFlexors/KneelingHipFlexor">Kneeling Hip Flexor Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipExternalRotators/SeatedHipExternalRotator">Seated Hip External Rotator Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Beine</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Quadriceps/SideLying">Lying (side) Quadriceps Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Hamstrings/LyingSingleLeg">Lying Hamstring Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Hamstrings/SeatedSingleLeg">Seated Single Leg Hamstring Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAdductors/SeatedGroinFloor">Seated Groin Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAdductors/SideLunge">Side Lunge Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Gastrocnemius/Wall">Wall Straight Leg Calf Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Soleus/Wall">Wall Bent Knee Calf Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/TibialisAnterior/Kneeling">Kneeling Shin Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+<script src="js/athletik.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/athletik-web/src/webui/training_lasse.html b/athletik-web/src/webui/training_lasse.html
new file mode 100644 (file)
index 0000000..3761e00
--- /dev/null
@@ -0,0 +1,1121 @@
+<!DOCTYPE html>
+<html lang="de">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="stylesheet"
+       href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
+<title>Kraft- und Ausdauertraining Lasse</title>
+<style type="text/css">
+BODY {
+       font-family: sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
+               'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
+               'Helvetica Neue';
+       line-height: 1.5;
+       font-size: 1rem;
+       margin: 0;
+}
+
+HEADER, SECTION {
+       margin-left: 15em;
+       padding: 0 0 0 1rem;
+}
+
+NAV {
+       background: #f0f0f0;
+       height: 100%;
+       position: fixed;
+       z-index: 1;
+       top: 0;
+       left: 0;
+       width: 15em;
+       overflow-x: hidden;
+}
+
+OL {
+       margin: 1.25em   0;
+}
+
+UL.navbar {
+       list-style-type: none;
+}
+
+.navbar li a {
+       text-decoration: none;
+       color: #333;
+       padding: 0.5rem 1rem;
+       display: block;
+       font-size: 2em;
+}
+
+.navbar li a:active, .navbar li a:hover {
+       background: #ddd;
+       transition: background 0.2s ease;
+}
+
+P.muscle {
+       border-top: 0.2em solid black;
+       padding: 0.25em 0 0 0;
+}
+
+P.start {
+       font-size: 1.25rem;
+       padding: 0 0 1.5em 0;
+}
+
+LI, P {
+       font-size: 1rem;
+}
+
+A {
+       text-decoration: none;
+}
+
+.musclename {
+       font-weight: 600;
+       color: #933;
+       background-color: #ddd;
+       padding: 0.5em;
+}
+
+.important {
+       font-weight: 600;
+       color: red;
+}
+
+.fa {
+       font-size: 1.5em;
+}
+
+H1 {
+       text-align: center;
+       font-size: 4.5em;
+}
+
+H2 {
+       text-align: center;
+       font-size: 3em;
+       text-decoration: underline overline;
+}
+
+.totop {
+       background-color: #666;
+       color: #ccc;
+       padding: 0.5em 2em;
+}
+
+.totop A {
+       color: #ccc;
+       font-size: 0.75em;
+}
+</style>
+</head>
+<body>
+
+       <header>
+               <h1 id="nav">Trainingsplan</h1>
+       </header>
+
+       <nav>
+               <ul class="navbar">
+                       <li><a href="#warmup">Aufwärmen</a></li>
+                       <li><a href="#general">Allgemeine Hinweise</a></li>
+                       <li><a href="#combinations">Sinnvolle Sessions</a></li>
+                       <li><a href="#arms">Arme</a></li>
+                       <li><a href="#shoulders">Schulter</a></li>
+                       <li><a href="#chest">Brust</a></li>
+                       <li><a href="#back">Rücken</a></li>
+                       <li><a href="#lowerbody">Rumpf</a></li>
+                       <li><a href="#hips">Hüfte & Gesäß</a></li>
+                       <li><a href="#legs">Beine</a></li>
+                       <li><a href="#cardio">Cardio</a></li>
+                       <li><a href="#stretching">Stretching</a></li>
+               </ul>
+       </nav>
+
+       <section id="warmup">
+               <h2>Warmup</h2>
+               <p class="start">Geräte: Crosstrainer oder Rudergerät</p>
+               <ol>
+                       <li>Kreislauf "starten": 10 min mindestens</li>
+                       <li>Mobilisierung: Alle Gelenke durchbewegen:
+                               <ul>
+                                       <li>Handgelenke kreisen</li>
+                                       <li>Armkreisen</li>
+                                       <li>Kopf/Hals</li>
+                                       <li>Oberkörper vor- und zurückbeugen</li>
+                                       <li>Hüftkreisen</li>
+                                       <li>2-3 mal Anfersen</li>
+                                       <li>Füße kreisen lassen</li>
+                               </ul>
+                       </li>
+               </ol>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="general">
+               <h2>Allgemeine Hinweise</h2>
+               <ul>
+                       <li>alles außer Rumpf:
+                               <ul><li>
+                       75% der Maximalkraft
+                       </li><li>Bewegung: Explosiv ausführen, 2 Sekunden zurückführen, 1-2
+                               Sekunden halten</li>
+                       <li>3 Sätze a 10 Wiederholungen</li>
+               </ul>
+               </li>
+               <li>Rumpf:
+                       <ul>
+                               <li>50% der Maximalkraft</li>
+                               <li>Bewegung: zügig in beide Richtungen ausführen, ohne halten</li>
+                               <li>3 Sätze a 20-30 Wiederholungen</li>
+                       </ul>
+               </li>
+               <li>Ca. 1 Min. Erholung zwischen den Sätzen.</li>
+               <li>Wenn die Bewegungsqualität unsauber wird: Abbruch der Übung!</li>
+               <li><span class="important">Scheiben auf der Langhantel
+                               immer mit Klammern sichern !!!</span></li>
+               <li>Begriffe:<br />Barbell: Langhantel<br />Bumbbell:
+                       Kurzhantel
+               </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="combinations">
+               <h2>Sinnvolle Sessions</h2>
+               <ul>
+                       <li><ol>
+                       <li><a href="#warmup">Aufwärmen</a></li>
+                       <li><a href="#arms">Arme</a></li>
+                       <li><a href="#shoulders">Schulter</a></li>
+                       <li><a href="#stretching">Stretching</a></li>
+                       </ol></li>
+                       <li><ol>
+                       <li><a href="#warmup">Aufwärmen</a></li>
+                       <li><a href="#chest">Brust</a></li>
+                       <li><a href="#back">Rücken</a></li>
+                       <li><a href="#lowerbody">Rumpf</a></li>
+                       <li><a href="#stretching">Stretching</a></li>
+                       </ol></li>
+                       <li><ol>
+                       <li><a href="#warmup">Aufwärmen</a></li>
+                       <li><a href="#hips">Hüfte & Gesäß</a></li>
+                       <li><a href="#legs">Beine</a></li>
+                       <li><a href="#stretching">Stretching</a></li>
+                       </ol></li>
+                       <li><ol>
+                       <li><a href="#warmup">Aufwärmen</a>, wenn Cardio im Gym</li>
+                       <li><a href="#cardio">Cardio</a></li>
+                       <li><a href="#stretching">Stretching</a></li>
+                       </ol></li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="arms">
+               <h2>Arme</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Triceps Brachii / Trizeps</span><br /> Bewegung:
+                       Armstreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Triceps/DBLyingTriExt">Dumbbell
+                                               Lying Triceps Extension <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Triceps/CBPushdown">Cable
+                                               Pushdown <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Biceps Brachii / Bizeps</span><br /> Bewegung:
+                       Armbeugung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Easy Bar Langhantel (die geschwungene Langhantel)<br /> <a
+                                               target="_blank"
+                                               href="https://exrx.net/WeightExercises/Biceps/BBCurl">Barbell
+                                               Curl <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Biceps/DBCurl">Dumbbell
+                                               Curl <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Biceps/CBCurl">Cable
+                                               Curl <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Brachialis</span><br /> Bewegung:
+                       Armbeugung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Brachialis/DBPreacherCurl">Dumbbell
+                                               Preacher Curl <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="shoulders">
+               <h2>Schulter</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Anterior Deltoid / Deltamuskel vorne</span><br />
+                       Bewegung: Oberarm anheben
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/DeltoidAnterior/DBShoulderPress">Dumbbell
+                                               Shoulder Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Posterior Deltoid / Deltamuskel hinten</span><br />
+                       Bewegung: Oberarm nach hinten ziehen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug (ggf. 2)<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/DeltoidPosterior/CBStandingReverseFly">Cable
+                                               Reverse Fly <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Lateral Deltoid / Deltamuskel seitlich</span><br />
+                       Bewegung: Oberarm anheben
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Langhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/DeltoidLateral/BBUprightRow">Barbell
+                                               Upright Row <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Supraspinatus</span><br />
+                       Bewegung: Oberarm schräg anheben
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Supraspinatus/DBFrontLateralRaise">Dumbbell
+                                               Front Lateral Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="chest">
+               <h2>Brust</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Pectoralis Major/ Brustmuskel groß, Ansatz am
+                               Brustbein</span><br /> Bewegung: Schulterstreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralSternal/DBBenchPress">Dumbbell
+                                               Bench Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Langhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralSternal/BBBenchPress">Barbell
+                                               Bench Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Pectoralis Major / Brustmuskel groß, Ansatz am
+                               Schlüsselbein</span><br /> Bewegung: Schulterstreckung <br /> <span
+                               class="important">Rückenlage auf einer schrägen (!) Bank</span>
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralClavicular/DBInclineBenchPress">Dumbbell
+                                               Incline Bench Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Langhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralClavicular/BBInclineBenchPress">Barbell
+                                               Incline Bench Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralClavicular/CBInclineFly">Cable
+                                               Incline Fly <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Pectoralis Minor / Brustmuskel klein</span><br />
+                       Bewegung: Schulterstreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/PectoralSternal/CBStandingFly">Cable
+                                               Standing Fly <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Serratus Anterior / Sägemuskel</span><br /> Bewegung:
+                       Schulterstreckung <br /> <span class="important">Rückenlage
+                               auf einer schrägen (!) Bank</span>
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Langhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/SerratusAnterior/BBInclineShoulderRaise">Barbell
+                                               Incline Shoulder Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/SerratusAnterior/DBInclineShoulderRaise">Dumbbell
+                                               Incline Shoulder Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="back">
+               <h2>Rücken</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Latissimus Dorsi</span><br /> Bewegung:
+                       Oberarm anlegen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Lat-Zug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/LatissimusDorsi/CBFrontPulldown">Cable
+                                               Pulldown <span class="fa">&#xf08e;</span>
+                                       </a> <br /> <span class="important">Keine Rückenlage oder mit
+                                               dem Körpergewicht arbeiten</span>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Trapezius / Trapezmuskel</span><br /> Bewegung:
+                       Schulter anheben
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/TrapeziusUpper/DBShrug">Dumbbell
+                                               Shrug <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Rücken</span><br /> Bewegung:
+                       Schultern zusammenziehen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Rudermaschine / Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/BackGeneral/CBStraightBackSeatedRow">Cable
+                                               Straight Back Seated Row <span class="fa">&#xf08e;</span> <br />
+                                               <span class="important">Keine Rückenlage oder mit dem
+                                                       Körpergewicht arbeiten; Rücken bleibt gerade</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/BackGeneral/DBBentOverRow">Dumbbell
+                                               Bent-over Row <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Infraspinatus</span><br />
+                       Bewegung: Außenrotation
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Infraspinatus/CBStandingExternalRotation">Cable
+                                               Standing Shoulder External Rotation <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Subscapularis</span><br />
+                       Bewegung: Innenrotation
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Subscapularis/CBStandingInternalRotation">Cable
+                                               Standing Shoulder Internal Rotation <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="lowerbody">
+               <h2>Rumpf</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Rectus Abdominis / gerade Bauchmuskeln</span><br />
+                       Bewegung: Rumpfbeugung<br /> <span class="important">Tip: Übungen für schräge und gerade Bauchmuskeln nicht direkt hintereinander</span>
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/RectusAbdominis/BWSitUpAD">Sit-up <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Ball<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/RectusAbdominis/BWBallCrunch">Ball Crunch (on stability ball) <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: TRX<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/RectusAbdominis/STJackknife">Suspended Jack-knife <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: schräge Bank<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/RectusAbdominis/BWInclineSitUp">Incline Sit-up <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Obliques / schräge Bauchmuskeln</span><br />
+                       Bewegung: Rumpfbeugung schräg<br /> <span class="important">Tip: Übungen für schräge und gerade Bauchmuskeln nicht direkt hintereinander</span>
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Bank<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Obliques/BWTwistingCrunch">Twisting Crunch <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Ball<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Obliques/BWTwistingBallCrunch">Twisting Crunch (on stability ball) <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: TRX<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Obliques/STTwistingJackknife">Suspended Twisting Jack-knife <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: schräge Bank<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Obliques/BWInclineTwistingSitUp">Incline Twisting Sit-up <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Erector Spinae / Rückenmuskel</span><br />
+                       Bewegung: Rumpfstreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/ErectorSpinae/BWAlternatingBirdDog">Alternating Bird Dog <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/ErectorSpinae/Superman">Superman <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Ball<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/ErectorSpinae/BWHyperextensionBall">Back Extension (on stability ball) <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/ErectorSpinae/DBOneArmStraightLegDeadlift">Dumbbell One Arm Straight Leg Deadlift <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: schräge Bank<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/ErectorSpinae/BW45HyperextensionHips">45° Hyperextension <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="hips">
+               <h2>Hüfte & Gesäß</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Gluteus Maximus / großer Gesäßmuskel</span><br />
+                       Bewegung: Hüftstreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/GluteusMaximus/BWSquat">Squat <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/GluteusMaximus/BWLunge">Lunges <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/GluteusMaximus/DBSplitSquat">Dumbbell Split Squat <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/GluteusMaximus/DBFrontSquat">Dumbbell Front Squat <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Gluteus Minor u.a. / kleiner Gesäßmuskel</span><br />
+                       Bewegung: Bein Abspreizen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/HipAbductor/BWSideBridgeHipAbduction">Side Bridge Hip Abduction <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/HipAbductor/CBHipAbduction">Cable Hip Abduction <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Piriformes u.a.</span><br />
+                       Bewegung: Beine schließen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/HipAdductors/CBHipAdduction">Cable Hip Adduction <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Rectus Femoris u.a. / Hüftbeuger</span><br />
+                       Bewegung: Hüftbeugung<br /><span class="important">Tip: Geht auch mit gestreckten Beinen</span>
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/HipFlexors/BWLyingLegRaise">Lying Leg Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/HipFlexors/CBLyingLegRaiseBench">Cable Lying Leg Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="legs">
+               <h2>Beine</h2>
+               <p class="start">Nur eine Übung je Bewegung / Muskel:</p>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Quadrizeps femoris / Oberschenkel</span><br />
+                       Bewegung: Kniestreckung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Beinpresse<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Quadriceps/SLHackSquat">Hack Squat <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Bizeps femoris u.a. / Unterschenkel</span><br />
+                       Bewegung: Kniebeugung
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kabelzug<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Hamstrings/CBLyingLegCurl">Cable Lying Leg Curl <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Gastrocnemius / zweibäuchiger Wadenmuskel</span><br />
+                       Bewegung: Fuß strecken / Knie beugen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Beinpresse<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Gastrocnemius/SLLyingCalfPress">Sled Lying Calf Press <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Gastrocnemius/DBSingleLegCalfRaise">Dumbbell Single Leg Calf Raise
+ <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Soleus / Schollenmuskel</span><br />
+                       Bewegung: Fuß strecken
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Multipresse<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Soleus/SMSeatedCalfRaise">Smith Seated Calf Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel: <span class="musclename">Tibialis Anterior / vorderer Schienbeinmuskel</span><br />
+                       Bewegung: Fuß anheben
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/TibialisAnterior/DBReverseCalfRaise">Dumbbell Reverse Calf Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Beinpresse<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/TibialisAnterior/SLHackReverseCalfRaise">Sled Hack Reverse Calf Raise <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="cardio">
+               <h2>Cardio</h2>
+               <p class="muscle">
+                       Laufen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Dauerläufe in ruhigem Tempo, so daß Du noch durch die Nase atmen kannst.<br />Dauer: 30-60 min
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Intervalläufe, 5-10 x 300-500m schnell und hart, 2-4 min Trab- / Gehpause.<br />Vorher 15-20 min einlaufen, anschließend 10 min auslaufen. Immer.
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Berganläufe, 5-10 x 1-2 min schnell und hart, 2-4 min Trab- / Gehpause.<br />Vorher 15-20 min einlaufen, anschließend 10 min auslaufen. Immer.
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Schwimmen
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Dauerschwimmen, 1000 bis ?? m in ruhigem Tempo in verschiedenen Lagen.
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       <a href="https://swim.triathlon-coaching.com">Schwimmtrainingspläne</a> (fast) ohne Ende...
+                               </p>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Zirkeltraining / CrossFit<br />Jede Übung 1 min, 1min Pause
+               </p>
+               <ul>
+                       <li>
+                               <p>
+                                       Gerät: Kettleball<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Kettlebell/KBTwoArmSwing">Kettlebell Two Arm Swing <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/Aerobic/Exercises/Burpee">Burpee <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Medizinball<br /> <a target="_blank"
+                                               href="https://exrx.net/Plyometrics/MBWallShot">Medicine Ball Wall Shot <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Box / Step<br /> <a target="_blank"
+                                               href="https://exrx.net/Plyometrics/BoxLateralJump">Lateral Box Jumps <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Medizinball<br /> <a target="_blank"
+                                               href="https://exrx.net/Plyometrics/MBChestThrowSitupWall">Medicine Ball Chest Throw Sit-up (on wall) <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Tau<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Power/RopeWave">Rope Wave <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: Kurzhantel<br /> <a target="_blank"
+                                               href="https://exrx.net/WeightExercises/Power/DBThruster">Dumbbell Thruster <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+                       <li>
+                               <p>
+                                       Gerät: none<br /> <a target="_blank"
+                                               href="https://exrx.net/Plyometrics/SplitJump">Split Jump <span class="fa">&#xf08e;</span>
+                                       </a>
+                               </p>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+       <section id="stretching">
+               <h2>Stretching</h2>
+               <p class="start">Jede Übung 3 mal (pro Seite)<br />Anpannen - Entspannen - Dehnen<br />Pflicht: Die Muskeln, die man auch trainiert hat, dehenen</p>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Hals</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Sternocleidomastoid/NeckRetraction">Neck Retraction Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Splenius/Neck">Neck Extensor Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Schulter</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/DeltoidAnterior/Doorway">Doorway Front Deltoid Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/DeltoidLateral/SideDelt">Side Deltoid Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/DeltoidPosterior/RearDelt">Rear Deltoid Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Arme</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Triceps/Overhead">Overhead Triceps Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Biceps/Seated">Seated Biceps Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Brachioradialis/Standing">Standing Brachioradialis Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/WristFlexors/Seated">Seated Wrist Flexor Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/WristExtensors/Single">Single Arm Wrist Extensor <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Rücken</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/BackGeneral/PCCrossHand">Lever Back Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/LatissimusDorsi/Overhead">Overhead Lat Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/LatissimusDorsi/StandingSideReach">Standing Side Reach Lat Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Rhomboids/Hugging">Hugging Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Infraspinatus/SideLying">Side Lying Infraspinatus <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Subscapularis/Doorway">Doorway Subscapularis Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Brust</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/ChestGeneral/StraightArm">Straight Arm Chest Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/PectoralisMinor/Wall">Wall Angel <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Bauch</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/RectusAbdominis/Prone">Lying (prone) Abdominal Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Obliques/LyingCrossover">Lying Crossover Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Obliques/Pretzel">Pretzel Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/ErectorSpinae/SeatedFloor">Seated Lower Back Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Hüfte</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/GluteusMaximus/LyingModified">Lying Glute Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/GluteusMaximus/Squatting">Squatting Glute Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAbductors/WallIliotibial">Wall Iliotibial Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAbductors/Pretzel">Seated Pretzel Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAbductors/SeatedHipInternalRotator">Seated Hip Internal Rotator Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAbductors/LyingCrossover">Lying Crossover Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipFlexors/KneelingHipFlexor">Kneeling Hip Flexor Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipExternalRotators/SeatedHipExternalRotator">Seated Hip External Rotator Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="muscle">
+                       Muskel(-gruppe): <span class="musclename">Beine</span>
+               </p>
+               <ul>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Quadriceps/SideLying">Lying (side) Quadriceps Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Hamstrings/LyingSingleLeg">Lying Hamstring Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Hamstrings/SeatedSingleLeg">Seated Single Leg Hamstring Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAdductors/SeatedGroinFloor">Seated Groin Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/HipAdductors/SideLunge">Side Lunge Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Gastrocnemius/Wall">Wall Straight Leg Calf Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/Soleus/Wall">Wall Bent Knee Calf Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+                       <li>
+                               <a target="_blank" href="https://exrx.net/Stretches/TibialisAnterior/Kneeling">Kneeling Shin Stretch <span class="fa">&#xf08e;</span></a>
+                       </li>
+               </ul>
+               <p class="totop">
+                       <a href="#nav">nach oben</a>
+               </p>
+       </section>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/athletik-webapp/AthletikWebApplication.java b/athletik-webapp/AthletikWebApplication.java
new file mode 100644 (file)
index 0000000..9ef386e
--- /dev/null
@@ -0,0 +1,21 @@
+package com.triathlon_coaching.product.athletik.web;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@ComponentScan({"com.triathlon_coaching.product.athletik.web", "com.triathlon_coaching.product.athletik.backend"})
+public class AthletikWebApplication extends SpringBootServletInitializer {
+
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+        return application.sources(FitnessWebApplication.class);
+    }
+    
+    public static void main(String[] args) {
+        SpringApplication.run(FitnessWebApplication.class, args);
+    }
+}
diff --git a/athletik-webapp/pom.xml b/athletik-webapp/pom.xml
new file mode 100644 (file)
index 0000000..c74a953
--- /dev/null
@@ -0,0 +1,132 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <artifactId>athletik.webapp</artifactId>
+       <packaging>war</packaging>
+
+       <name>${project.artifactId}</name>
+       <description>Athletik Training Webapp</description>
+
+       <parent>
+               <groupId>com.triathlon-coaching.product</groupId>
+               <artifactId>athletik</artifactId>
+               <version>0.0.1-SNAPSHOT</version>
+       </parent>
+
+
+       <dependencyManagement>
+               <dependencies>
+
+                       <!-- Thymeleaf -->
+                       <!-- WebUI-->
+                       <dependency>
+                               <groupId>org.thymeleaf</groupId>
+                               <artifactId>thymeleaf-spring5</artifactId>
+                               <version>${thymeleaf.version}</version>
+                       </dependency>
+
+                       <!-- JEE -->
+                       <dependency>
+                               <groupId>javax.servlet</groupId>
+                               <artifactId>javax.servlet-api</artifactId>
+                               <version>${javax.servlet-api.version}</version>
+                               <scope>provided</scope>
+                       </dependency>
+
+                       <!-- Web (für REST API) -->
+                       <dependency>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-starter-web</artifactId>
+                               <version>${spring-boot.version}</version>
+                       </dependency>
+
+                       <!-- Tomcat Embed (for testing) -->
+                       <dependency>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-starter-tomcat</artifactId>
+                               <scope>provided</scope>
+                               <version>${spring-boot.version}</version>
+                       </dependency>
+
+               </dependencies>
+       </dependencyManagement>
+
+
+       <dependencies>
+               <!-- Backend Module -->
+               <dependency>
+                       <groupId>com.triathlon-coaching.product</groupId>
+                       <artifactId>athletik.service</artifactId>
+                       <version>${project.version}</version>
+               </dependency>
+
+               <!-- Thymeleaf -->
+               <!-- WebUI-->
+               <dependency>
+                       <groupId>org.thymeleaf</groupId>
+                       <artifactId>thymeleaf-spring5</artifactId>
+               </dependency>
+
+               <!-- Web -->
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-web</artifactId>
+               </dependency>
+
+               <!-- Servlet API (provided by Tomcat) -->
+               <dependency>
+                       <groupId>javax.servlet</groupId>
+                       <artifactId>javax.servlet-api</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Tomcat Embed (for testing) -->
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-tomcat</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+       </dependencies>
+
+       <build>
+               <finalName>fitness-web</finalName>
+               <pluginManagement>
+                       <plugins>
+                               <plugin>
+                                       <groupId>org.springframework.boot</groupId>
+                                       <artifactId>spring-boot-maven-plugin</artifactId>
+                                       <version>${spring-boot.version}</version>
+                               </plugin>
+                       </plugins>
+               </pluginManagement>
+               <plugins>
+                       <plugin>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-maven-plugin</artifactId>
+                               <configuration>
+                                       <mainClass>com.fitnessapp.web.FitnessWebApplication</mainClass>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+
+       <profiles>
+               <profile>
+                       <id>dev</id>
+                       <activation>
+                               <activeByDefault>true</activeByDefault>
+                       </activation>
+                       <properties>
+                               <spring.profiles.active>dev</spring.profiles.active>
+                       </properties>
+               </profile>
+               <profile>
+                       <id>prod</id>
+                       <properties>
+                               <spring.profiles.active>prod</spring.profiles.active>
+                       </properties>
+               </profile>
+       </profiles>
+
+</project>
diff --git a/athletik-webapp/src/main/webapp/img/Big_Ben_London_closeup.jpg b/athletik-webapp/src/main/webapp/img/Big_Ben_London_closeup.jpg
new file mode 100644 (file)
index 0000000..c494e47
Binary files /dev/null and b/athletik-webapp/src/main/webapp/img/Big_Ben_London_closeup.jpg differ
diff --git a/athletik-webapp/src/main/webapp/img/Reichstag_Berlin.jpg b/athletik-webapp/src/main/webapp/img/Reichstag_Berlin.jpg
new file mode 100644 (file)
index 0000000..250d271
Binary files /dev/null and b/athletik-webapp/src/main/webapp/img/Reichstag_Berlin.jpg differ
diff --git a/athletik-webapp/src/main/webapp/index.html b/athletik-webapp/src/main/webapp/index.html
new file mode 100644 (file)
index 0000000..5987a4a
--- /dev/null
@@ -0,0 +1,474 @@
+<!DOCTYPE html>
+<html lang="de" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Athletik Training - Startseite</title>
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
+    <link rel="stylesheet" href="css/athletik.css">
+    <style>
+        .hero-section {
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            color: white;
+            padding: 80px 0;
+            text-align: center;
+        }
+        .feature-card {
+            border: none;
+            border-radius: 15px;
+            box-shadow: 0 4px 15px rgba(0,0,0,0.1);
+            transition: transform 0.3s ease;
+            height: 100%;
+        }
+        .feature-card:hover {
+            transform: translateY(-5px);
+        }
+        .feature-icon {
+            font-size: 3rem;
+            margin-bottom: 1rem;
+            color: #667eea;
+        }
+        .stats-number {
+            font-size: 2.5rem;
+            font-weight: bold;
+            color: #667eea;
+        }
+        .testimonial-card {
+            background: #f8f9fa;
+            border-radius: 15px;
+            padding: 2rem;
+            margin: 1rem 0;
+        }
+        .cta-section {
+            background: #f8f9fa;
+            padding: 60px 0;
+        }
+        .muscle-group-card {
+            background: linear-gradient(135deg, #4ecdc4 0%, #44a08d 100%);
+            color: white;
+            border-radius: 15px;
+            padding: 20px;
+            text-align: center;
+            margin-bottom: 20px;
+            min-height: 120px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            transition: transform 0.3s ease;
+        }
+        .muscle-group-card:hover {
+            transform: scale(1.05);
+        }
+        .navbar-brand {
+            font-weight: bold;
+            font-size: 1.5rem;
+        }
+        @media (max-width: 768px) {
+            .hero-section {
+                padding: 60px 0;
+            }
+            .feature-icon {
+                font-size: 2.5rem;
+            }
+        }
+    </style>
+</head>
+<body>
+    <!-- Navigation -->
+    <nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top">
+        <div class="container">
+            <a class="navbar-brand" href="/">
+                <i class="fas fa-dumbbell me-2"></i>Athletik Training
+            </a>
+            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
+                <span class="navbar-toggler-icon"></span>
+            </button>
+            <div class="collapse navbar-collapse" id="navbarNav">
+                <ul class="navbar-nav ms-auto">
+                    <li class="nav-item">
+                        <a class="nav-link" href="#features">Features</a>
+                    </li>
+                    <li class="nav-item">
+                        <a class="nav-link" href="#musclegroups">Muskelgruppen</a>
+                    </li>
+                    <li class="nav-item">
+                        <a class="nav-link" href="#testimonials">Erfolge</a>
+                    </li>
+                    <li class="nav-item">
+                        <a class="nav-link" href="#about">Über uns</a>
+                    </li>
+                    <li class="nav-item">
+                        <a class="btn btn-outline-light ms-2" href="/login">
+                            <i class="fas fa-sign-in-alt"></i> Login
+                        </a>
+                    </li>
+                    <li class="nav-item">
+                        <a class="btn btn-primary ms-2" href="/login?register=true">
+                            <i class="fas fa-user-plus"></i> Registrieren
+                        </a>
+                    </li>
+                </ul>
+            </div>
+        </div>
+    </nav>
+
+    <!-- Hero Section -->
+    <section class="hero-section">
+        <div class="container">
+            <div class="row">
+                <div class="col-lg-8 mx-auto">
+                    <h1 class="display-4 fw-bold mb-4">Dein digitaler Trainingspartner</h1>
+                    <p class="lead mb-4">Protokolliere deine Workouts, tracke deine Fortschritte und erreiche deine Fitnessziele mit unserer intelligenten Plattform.</p>
+                    <div class="d-flex gap-3 justify-content-center flex-wrap">
+                        <a href="/login?register=true" class="btn btn-light btn-lg">
+                            <i class="fas fa-rocket me-2"></i>Jetzt starten
+                        </a>
+                        <a href="#features" class="btn btn-outline-light btn-lg">
+                            <i class="fas fa-info-circle me-2"></i>Mehr erfahren
+                        </a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </section>
+
+    <!-- Stats Section -->
+    <section class="py-5 bg-light">
+        <div class="container">
+            <div class="row text-center">
+                <div class="col-md-3 col-6 mb-4">
+                    <div class="stats-number" id="totalUsers">5.000+</div>
+                    <p class="text-muted">Aktive Nutzer</p>
+                </div>
+                <div class="col-md-3 col-6 mb-4">
+                    <div class="stats-number" id="totalWorkouts">250.000+</div>
+                    <p class="text-muted">Protokollierte Workouts</p>
+                </div>
+                <div class="col-md-3 col-6 mb-4">
+                    <div class="stats-number" id="totalExercises">200+</div>
+                    <p class="text-muted">Übungen</p>
+                </div>
+                <div class="col-md-3 col-6 mb-4">
+                    <div class="stats-number" id="successRate">98%</div>
+                    <p class="text-muted">Zufriedene Athleten</p>
+                </div>
+            </div>
+        </div>
+    </section>
+
+    <!-- Features Section -->
+    <section id="features" class="py-5">
+        <div class="container">
+            <div class="text-center mb-5">
+                <h2 class="display-5 fw-bold">Warum Athletik Training?</h2>
+                <p class="lead text-muted">Alles, was du für deinen Trainingserfolg brauchst</p>
+            </div>
+            
+            <div class="row g-4">
+                <div class="col-lg-4 col-md-6">
+                    <div class="card feature-card h-100">
+                        <div class="card-body text-center p-4">
+                            <div class="feature-icon">
+                                <i class="fas fa-clipboard-list"></i>
+                            </div>
+                            <h4>Intuitive Protokollierung</h4>
+                            <p class="text-muted">Erfasse deine Trainingseinheiten mühelos mit unserem benutzerfreundlichen Interface.</p>
+                        </div>
+                    </div>
+                </div>
+                
+                <div class="col-lg-4 col-md-6">
+                    <div class="card feature-card h-100">
+                        <div class="card-body text-center p-4">
+                            <div class="feature-icon">
+                                <i class="fas fa-chart-line"></i>
+                            </div>
+                            <h4>Fortschritt tracking</h4>
+                            <p class="text-muted">Verfolge deine Leistungssteigerung mit detaillierten Statistiken und Diagrammen.</p>
+                        </div>
+                    </div>
+                </div>
+                
+                <div class="col-lg-4 col-md-6">
+                    <div class="card feature-card h-100">
+                        <div class="card-body text-center p-4">
+                            <div class="feature-icon">
+                                <i class="fas fa-dumbbell"></i>
+                            </div>
+                            <h4>Umfangreiche Übungsdatenbank</h4>
+                            <p class="text-muted">Über 200 Übungen mit Videoanleitungen und Muskelgruppen-Zuordnung.</p>
+                        </div>
+                    </div>
+                </div>
+                
+                <div class="col-lg-4 col-md-6">
+                    <div class="card feature-card h-100">
+                        <div class="card-body text-center p-4">
+                            <div class="feature-icon">
+                                <i class="fas fa-calculator"></i>
+                            </div>
+                            <h4>1RM Berechnung</h4>
+                            <p class="text-muted">Automatische Berechnung deiner Maximalkraft basierend auf deinen Trainingseinheiten.</p>
+                        </div>
+                    </div>
+                </div>
+                
+                <div class="col-lg-4 col-md-6">
+                    <div class="card feature-card h-100">
+                        <div class="card-body text-center p-4">
+                            <div class="feature-icon">
+                                <i class="fas fa-envelope"></i>
+                            </div>
+                            <h4>E-Mail Zusammenfassungen</h4>
+                            <p class="text-muted">Erhalte detaillierte Trainingszusammenfassungen per E-Mail nach jeder Einheit.</p>
+                        </div>
+                    </div>
+                </div>
+                
+                <div class="col-lg-4 col-md-6">
+                    <div class="card feature-card h-100">
+                        <div class="card-body text-center p-4">
+                            <div class="feature-icon">
+                                <i class="fas fa-mobile-alt"></i>
+                            </div>
+                            <h4>Mobile Optimiert</h4>
+                            <p class="text-muted">Perfekt für die Nutzung im Fitnessstudio auf deinem Smartphone oder Tablet.</p>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </section>
+
+    <!-- Muscle Groups Section -->
+    <section id="musclegroups" class="py-5 bg-light">
+        <div class="container">
+            <div class="text-center mb-5">
+                <h2 class="display-5 fw-bold">Muskelgruppen</h2>
+                <p class="lead text-muted">Trainiere gezielt alle Muskelgruppen</p>
+            </div>
+            
+            <div class="row" id="muscleGroupsContainer">
+                <!-- Wird dynamisch gefüllt -->
+            </div>
+        </div>
+    </section>
+
+    <!-- Testimonials Section -->
+    <section id="testimonials" class="py-5">
+        <div class="container">
+            <div class="text-center mb-5">
+                <h2 class="display-5 fw-bold">Erfolgsgeschichten</h2>
+                <p class="lead text-muted">Was unsere Athleten sagen</p>
+            </div>
+            
+            <div class="row">
+                <div class="col-lg-4 mb-4">
+                    <div class="testimonial-card text-center">
+                        <div class="mb-3">
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                        </div>
+                        <p class="fst-italic">"Durch das präzise Tracking meiner Trainingseinheiten konnte ich meine Maximalkraft in 3 Monaten um 25% steigern!"</p>
+                        <h6>Markus T.</h6>
+                        <small class="text-muted">Seit 6 Monaten dabei</small>
+                    </div>
+                </div>
+                
+                <div class="col-lg-4 mb-4">
+                    <div class="testimonial-card text-center">
+                        <div class="mb-3">
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                        </div>
+                        <p class="fst-italic">"Die E-Mail-Zusammenfassungen helfen mir, meinen Fortschritt im Blick zu behalten und motivieren mich weiterzumachen."</p>
+                        <h6>Sarah K.</h6>
+                        <small class="text-muted">Seit 1 Jahr dabei</small>
+                    </div>
+                </div>
+                
+                <div class="col-lg-4 mb-4">
+                    <div class="testimonial-card text-center">
+                        <div class="mb-3">
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                            <i class="fas fa-star text-warning"></i>
+                        </div>
+                        <p class="fst-italic">"Die Übungsdatenbank mit Videoanleitungen hat mein Training revolutioniert. Endlich mache ich jede Übung korrekt!"</p>
+                        <h6>Thomas R.</h6>
+                        <small class="text-muted">Seit 3 Monaten dabei</small>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </section>
+
+    <!-- CTA Section -->
+    <section id="cta" class="cta-section">
+        <div class="container">
+            <div class="row">
+                <div class="col-lg-8 mx-auto text-center">
+                    <h2 class="display-6 fw-bold mb-4">Bereit, dein Training zu transformieren?</h2>
+                    <p class="lead mb-4">Starte noch heute und werde Teil unserer Community von erfolgreichen Athleten.</p>
+                    <a href="/login?register=true" class="btn btn-primary btn-lg px-5">
+                        <i class="fas fa-user-plus me-2"></i>Kostenlos registrieren
+                    </a>
+                </div>
+            </div>
+        </div>
+    </section>
+
+    <!-- Footer -->
+    <footer class="bg-dark text-white py-4">
+        <div class="container">
+            <div class="row">
+                <div class="col-lg-4 mb-4">
+                    <h5><i class="fas fa-dumbbell me-2"></i>Athletik Training</h5>
+                    <p class="text-muted">Dein digitaler Partner für maximale Trainingserfolge.</p>
+                </div>
+                <div class="col-lg-2 col-6 mb-4">
+                    <h6>Navigation</h6>
+                    <ul class="list-unstyled">
+                        <li><a href="#features" class="text-muted text-decoration-none">Features</a></li>
+                        <li><a href="#musclegroups" class="text-muted text-decoration-none">Muskelgruppen</a></li>
+                        <li><a href="#testimonials" class="text-muted text-decoration-none">Erfolge</a></li>
+                    </ul>
+                </div>
+                <div class="col-lg-2 col-6 mb-4">
+                    <h6>Rechtliches</h6>
+                    <ul class="list-unstyled">
+                        <li><a href="#" class="text-muted text-decoration-none">Datenschutz</a></li>
+                        <li><a href="#" class="text-muted text-decoration-none">Impressum</a></li>
+                        <li><a href="#" class="text-muted text-decoration-none">AGB</a></li>
+                    </ul>
+                </div>
+                <div class="col-lg-4 mb-4">
+                    <h6>Kontakt</h6>
+                    <p class="text-muted mb-1">
+                        <i class="fas fa-envelope me-2"></i>support@athletik-training.de
+                    </p>
+                    <div class="d-flex gap-3 mt-3">
+                        <a href="#" class="text-muted"><i class="fab fa-facebook fa-lg"></i></a>
+                        <a href="#" class="text-muted"><i class="fab fa-instagram fa-lg"></i></a>
+                        <a href="#" class="text-muted"><i class="fab fa-twitter fa-lg"></i></a>
+                    </div>
+                </div>
+            </div>
+            <hr class="my-4">
+            <div class="text-center">
+                <p class="text-muted mb-0">&copy; 2024 Athletik Training. Alle Rechte vorbehalten.</p>
+            </div>
+        </div>
+    </footer>
+
+    <!-- JavaScript -->
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+    <script>
+        // Muskelgruppen laden
+        async function loadMuscleGroups() {
+            try {
+                const response = await fetch('/api/muscles/groups');
+                if (response.ok) {
+                    const muscleGroups = await response.json();
+                    displayMuscleGroups(muscleGroups);
+                }
+            } catch (error) {
+                console.error('Error loading muscle groups:', error);
+            }
+        }
+
+        function displayMuscleGroups(muscleGroups) {
+            const container = document.getElementById('muscleGroupsContainer');
+            const muscleGroupNames = {
+                'Arms': 'Oberarme',
+                'Shoulders': 'Schultern', 
+                'Back': 'Rücken',
+                'Chest': 'Brust',
+                'Core': 'Rumpf',
+                'Hips': 'Hüfte',
+                'Legs': 'Beine',
+                'Calves': 'Waden'
+            };
+
+            const icons = {
+                'Arms': 'fas fa-hand-fist',
+                'Shoulders': 'fas fa-arrows-alt-h',
+                'Back': 'fas fa-archway',
+                'Chest': 'fas fa-tshirt',
+                'Core': 'fas fa-circle',
+                'Hips': 'fas fa-hip',
+                'Legs': 'fas fa-walking',
+                'Calves': 'fas fa-shoe-prints'
+            };
+
+            muscleGroups.forEach(group => {
+                const col = document.createElement('div');
+                col.className = 'col-md-3 col-6 mb-3';
+                col.innerHTML = `
+                    <div class="muscle-group-card">
+                        <div>
+                            <i class="${icons[group] || 'fas fa-dumbbell'} fa-2x mb-2"></i>
+                            <h6 class="mb-0">${muscleGroupNames[group] || group}</h6>
+                        </div>
+                    </div>
+                `;
+                container.appendChild(col);
+            });
+        }
+
+        // Statistikdaten laden
+        async function loadStatistics() {
+            try {
+                const response = await fetch('/api/stats');
+                if (response.ok) {
+                    const stats = await response.json();
+                    updateStatistics(stats);
+                }
+            } catch (error) {
+                console.error('Error loading statistics:', error);
+            }
+        }
+
+        function updateStatistics(stats) {
+            if (stats.totalUsers) {
+                document.getElementById('totalUsers').textContent = stats.totalUsers.toLocaleString() + '+';
+            }
+            if (stats.totalWorkouts) {
+                document.getElementById('totalWorkouts').textContent = stats.totalWorkouts.toLocaleString() + '+';
+            }
+            if (stats.totalExercises) {
+                document.getElementById('totalExercises').textContent = stats.totalExercises + '+';
+            }
+        }
+
+        // Smooth Scroll für Navigation
+        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
+            anchor.addEventListener('click', function (e) {
+                e.preventDefault();
+                const target = document.querySelector(this.getAttribute('href'));
+                if (target) {
+                    target.scrollIntoView({
+                        behavior: 'smooth',
+                        block: 'start'
+                    });
+                }
+            });
+        });
+
+        // Initialisierung
+        document.addEventListener('DOMContentLoaded', function() {
+            loadMuscleGroups();
+            loadStatistics();
+        });
+    </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/athletik-webapp/src/main/webapp/js/athletik.js b/athletik-webapp/src/main/webapp/js/athletik.js
new file mode 100644 (file)
index 0000000..ebcbebb
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * 
+ */
+
+document.addEventListener('DOMContentLoaded', function() {
+    const forms = document.querySelectorAll('.ajax-form');
+    
+    forms.forEach(form => {
+        form.addEventListener('submit', async function(e) {
+            e.preventDefault();
+            
+            const formData = new FormData(this);
+            const submitButton = this.querySelector('button[type="submit"]');
+            // Loading-State
+            submitButton.disabled = true;
+            submitButton.textContent = 'saving ...';
+            
+            try {
+                const response = await fetch('save-data.php', {
+                    method: 'POST',
+                    body: formData,
+                                       credentials: 'include', // Cookies mitsenden
+                                   headers: {
+                                       'X-Requested-With': 'XMLHttpRequest'
+                                   }
+                });
+                
+                const result = await response.json();
+                
+                if (result.success) {
+                    showMessage('Data saved successfully!', 'success');
+                    this.reset(); // Formular zurücksetzen
+                } else {
+                    showMessage('Error: ' + result.message, 'error');
+                }
+                
+            } catch (error) {
+                showMessage('Network error: ' + error.message, 'error');
+            } finally {
+                // Button zurücksetzen
+                submitButton.disabled = false;
+                submitButton.textContent = 'Save';
+            }
+        });
+    });
+    
+    function showMessage(text, type) {
+        const messageDiv = document.getElementById('responseMessage');
+        messageDiv.textContent = text;
+        messageDiv.className = type;
+        messageDiv.style.display = 'block';
+        
+        // Nachricht nach 5 Sekunden ausblenden
+        setTimeout(() => {
+            messageDiv.style.display = 'none';
+        }, 5000);
+    }
+});
\ No newline at end of file
diff --git a/athletik-webapp/src/main/webapp/login.html b/athletik-webapp/src/main/webapp/login.html
new file mode 100644 (file)
index 0000000..86b2a84
--- /dev/null
@@ -0,0 +1,479 @@
+<!DOCTYPE html>
+<html lang="de">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Login - Athletik Training</title>
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
+    <link rel="stylesheet" href="css/athletik.css">
+    <style>
+        .login-container {
+            min-height: 100vh;
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            padding: 20px;
+        }
+        .login-card {
+            background: white;
+            border-radius: 20px;
+            box-shadow: 0 15px 35px rgba(0,0,0,0.1);
+            width: 100%;
+            max-width: 400px;
+            overflow: hidden;
+        }
+        .login-header {
+            background: linear-gradient(135deg, #4ecdc4 0%, #44a08d 100%);
+            color: white;
+            text-align: center;
+            padding: 30px 20px;
+        }
+        .login-body {
+            padding: 30px;
+        }
+        .form-control {
+            border-radius: 10px;
+            padding: 12px 20px;
+            border: 2px solid #e9ecef;
+            transition: all 0.3s ease;
+        }
+        .form-control:focus {
+            border-color: #667eea;
+            box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
+        }
+        .btn-login {
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            border: none;
+            border-radius: 10px;
+            padding: 12px;
+            color: white;
+            font-weight: 600;
+            transition: all 0.3s ease;
+        }
+        .btn-login:hover {
+            transform: translateY(-2px);
+            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
+        }
+        .social-login {
+            display: flex;
+            gap: 10px;
+            margin: 20px 0;
+        }
+        .btn-social {
+            flex: 1;
+            border-radius: 8px;
+            padding: 10px;
+            text-align: center;
+            border: 1px solid #dee2e6;
+            transition: all 0.3s ease;
+        }
+        .btn-social:hover {
+            transform: translateY(-1px);
+        }
+        .register-link {
+            color: #667eea;
+            text-decoration: none;
+            font-weight: 600;
+        }
+        .register-link:hover {
+            text-decoration: underline;
+        }
+        .password-toggle {
+            cursor: pointer;
+            position: absolute;
+            right: 15px;
+            top: 50%;
+            transform: translateY(-50%);
+            color: #6c757d;
+        }
+        .input-group {
+            position: relative;
+        }
+        .alert {
+            border-radius: 10px;
+            border: none;
+        }
+        @media (max-width: 576px) {
+            .login-container {
+                padding: 10px;
+            }
+            .login-body {
+                padding: 20px;
+            }
+        }
+    </style>
+</head>
+<body>
+    <div class="login-container">
+        <div class="login-card">
+            <!-- Header -->
+            <div class="login-header">
+                <i class="fas fa-dumbbell fa-3x mb-3"></i>
+                <h3 class="mb-1">Athletik Training</h3>
+                <p class="mb-0">Willkommen zurück</p>
+            </div>
+
+            <!-- Body -->
+            <div class="login-body">
+                <!-- Login Form -->
+                <form id="loginForm" style="display: block;">
+                    <div class="alert alert-danger" id="loginError" style="display: none;">
+                        <i class="fas fa-exclamation-circle me-2"></i>
+                        <span id="errorMessage"></span>
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="username" class="form-label">Benutzername</label>
+                        <div class="input-group">
+                            <span class="input-group-text">
+                                <i class="fas fa-user"></i>
+                            </span>
+                            <input type="text" class="form-control" id="username" placeholder="Dein Benutzername" required>
+                        </div>
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="password" class="form-label">Passwort</label>
+                        <div class="input-group">
+                            <span class="input-group-text">
+                                <i class="fas fa-lock"></i>
+                            </span>
+                            <input type="password" class="form-control" id="password" placeholder="Dein Passwort" required>
+                            <span class="password-toggle" onclick="togglePassword()">
+                                <i class="fas fa-eye"></i>
+                            </span>
+                        </div>
+                    </div>
+
+                    <div class="mb-3 form-check">
+                        <input type="checkbox" class="form-check-input" id="rememberMe">
+                        <label class="form-check-label" for="rememberMe">Eingeloggt bleiben</label>
+                        <a href="#forgot-password" class="float-end text-decoration-none" onclick="showForgotPassword()">
+                            Passwort vergessen?
+                        </a>
+                    </div>
+
+                    <button type="submit" class="btn btn-login w-100 mb-3">
+                        <i class="fas fa-sign-in-alt me-2"></i>Anmelden
+                    </button>
+
+                    <div class="text-center">
+                        <p class="mb-0">Noch keinen Account? 
+                            <a href="#" class="register-link" onclick="showRegister()">Jetzt registrieren</a>
+                        </p>
+                    </div>
+                </form>
+
+                <!-- Register Form -->
+                <form id="registerForm" style="display: none;">
+                    <div class="alert alert-danger" id="registerError" style="display: none;">
+                        <i class="fas fa-exclamation-circle me-2"></i>
+                        <span id="registerErrorMessage"></span>
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="regUsername" class="form-label">Benutzername</label>
+                        <div class="input-group">
+                            <span class="input-group-text">
+                                <i class="fas fa-user"></i>
+                            </span>
+                            <input type="text" class="form-control" id="regUsername" placeholder="Wähle einen Benutzernamen" required>
+                        </div>
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="regFullname" class="form-label">Vollständiger Name</label>
+                        <div class="input-group">
+                            <span class="input-group-text">
+                                <i class="fas fa-id-card"></i>
+                            </span>
+                            <input type="text" class="form-control" id="regFullname" placeholder="Vor- und Nachname" required>
+                        </div>
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="regEmail" class="form-label">E-Mail Adresse</label>
+                        <div class="input-group">
+                            <span class="input-group-text">
+                                <i class="fas fa-envelope"></i>
+                            </span>
+                            <input type="email" class="form-control" id="regEmail" placeholder="deine@email.de" required>
+                        </div>
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="regPassword" class="form-label">Passwort</label>
+                        <div class="input-group">
+                            <span class="input-group-text">
+                                <i class="fas fa-lock"></i>
+                            </span>
+                            <input type="password" class="form-control" id="regPassword" placeholder="Sicheres Passwort" required>
+                            <span class="password-toggle" onclick="toggleRegisterPassword()">
+                                <i class="fas fa-eye"></i>
+                            </span>
+                        </div>
+                        <div class="form-text">Mindestens 8 Zeichen, Groß-/Kleinbuchstaben und Zahlen</div>
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="regPasswordConfirm" class="form-label">Passwort bestätigen</label>
+                        <div class="input-group">
+                            <span class="input-group-text">
+                                <i class="fas fa-lock"></i>
+                            </span>
+                            <input type="password" class="form-control" id="regPasswordConfirm" placeholder="Passwort wiederholen" required>
+                        </div>
+                    </div>
+
+                    <div class="mb-3 form-check">
+                        <input type="checkbox" class="form-check-input" id="acceptTerms" required>
+                        <label class="form-check-label" for="acceptTerms">
+                            Ich akzeptiere die <a href="#" class="text-decoration-none">AGB</a> und 
+                            <a href="#" class="text-decoration-none">Datenschutzbestimmungen</a>
+                        </label>
+                    </div>
+
+                    <button type="submit" class="btn btn-login w-100 mb-3">
+                        <i class="fas fa-user-plus me-2"></i>Registrieren
+                    </button>
+
+                    <div class="text-center">
+                        <p class="mb-0">Bereits Account? 
+                            <a href="#" class="register-link" onclick="showLogin()">Zum Login</a>
+                        </p>
+                    </div>
+                </form>
+
+                <!-- Forgot Password Form -->
+                <form id="forgotPasswordForm" style="display: none;">
+                    <div class="alert alert-info">
+                        <i class="fas fa-info-circle me-2"></i>
+                        Gib deine E-Mail Adresse ein, um ein neues Passwort anzufordern.
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="forgotEmail" class="form-label">E-Mail Adresse</label>
+                        <div class="input-group">
+                            <span class="input-group-text">
+                                <i class="fas fa-envelope"></i>
+                            </span>
+                            <input type="email" class="form-control" id="forgotEmail" placeholder="deine@email.de" required>
+                        </div>
+                    </div>
+
+                    <button type="submit" class="btn btn-login w-100 mb-3">
+                        <i class="fas fa-paper-plane me-2"></i>Passwort anfordern
+                    </button>
+
+                    <div class="text-center">
+                        <a href="#" class="register-link" onclick="showLogin()">
+                            <i class="fas fa-arrow-left me-1"></i>Zurück zum Login
+                        </a>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+    <script>
+        // Formular-Wechsel
+        function showRegister() {
+            document.getElementById('loginForm').style.display = 'none';
+            document.getElementById('registerForm').style.display = 'block';
+            document.getElementById('forgotPasswordForm').style.display = 'none';
+            resetErrors();
+        }
+
+        function showLogin() {
+            document.getElementById('loginForm').style.display = 'block';
+            document.getElementById('registerForm').style.display = 'none';
+            document.getElementById('forgotPasswordForm').style.display = 'none';
+            resetErrors();
+        }
+
+        function showForgotPassword() {
+            document.getElementById('loginForm').style.display = 'none';
+            document.getElementById('registerForm').style.display = 'none';
+            document.getElementById('forgotPasswordForm').style.display = 'block';
+            resetErrors();
+        }
+
+        function resetErrors() {
+            document.getElementById('loginError').style.display = 'none';
+            document.getElementById('registerError').style.display = 'none';
+        }
+
+        // Password Toggle
+        function togglePassword() {
+            const passwordInput = document.getElementById('password');
+            const toggleIcon = document.querySelector('.password-toggle i');
+            
+            if (passwordInput.type === 'password') {
+                passwordInput.type = 'text';
+                toggleIcon.className = 'fas fa-eye-slash';
+            } else {
+                passwordInput.type = 'password';
+                toggleIcon.className = 'fas fa-eye';
+            }
+        }
+
+        function toggleRegisterPassword() {
+            const passwordInput = document.getElementById('regPassword');
+            const toggleIcon = document.querySelector('#registerForm .password-toggle i');
+            
+            if (passwordInput.type === 'password') {
+                passwordInput.type = 'text';
+                toggleIcon.className = 'fas fa-eye-slash';
+            } else {
+                passwordInput.type = 'password';
+                toggleIcon.className = 'fas fa-eye';
+            }
+        }
+
+        // Login Form Submission
+        document.getElementById('loginForm').addEventListener('submit', async function(e) {
+            e.preventDefault();
+            
+            const username = document.getElementById('username').value;
+            const password = document.getElementById('password').value;
+            const rememberMe = document.getElementById('rememberMe').checked;
+
+            try {
+                const response = await fetch('/api/auth/login', {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/x-www-form-urlencoded',
+                    },
+                    body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}&remember-me=${rememberMe}`
+                });
+
+                if (response.ok) {
+                    window.location.href = '/training';
+                } else {
+                    const error = await response.text();
+                    showLoginError(error);
+                }
+            } catch (error) {
+                showLoginError('Netzwerkfehler. Bitte versuche es später erneut.');
+            }
+        });
+
+        // Register Form Submission
+        document.getElementById('registerForm').addEventListener('submit', async function(e) {
+            e.preventDefault();
+            
+            if (!validateRegistration()) {
+                return;
+            }
+
+            const userData = {
+                username: document.getElementById('regUsername').value,
+                fullname: document.getElementById('regFullname').value,
+                email: document.getElementById('regEmail').value,
+                pass: document.getElementById('regPassword').value
+            };
+
+            try {
+                const response = await fetch('/api/auth/register', {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json',
+                    },
+                    body: JSON.stringify(userData)
+                });
+
+                if (response.ok) {
+                    const user = await response.json();
+                    showRegisterSuccess();
+                } else {
+                    const error = await response.text();
+                    showRegisterError(error);
+                }
+            } catch (error) {
+                showRegisterError('Netzwerkfehler. Bitte versuche es später erneut.');
+            }
+        });
+
+        // Forgot Password Form Submission
+        document.getElementById('forgotPasswordForm').addEventListener('submit', async function(e) {
+            e.preventDefault();
+            
+            const email = document.getElementById('forgotEmail').value;
+            
+            try {
+                const response = await fetch('/api/auth/forgot-password', {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json',
+                    },
+                    body: JSON.stringify({ email: email })
+                });
+
+                if (response.ok) {
+                    alert('Eine E-Mail mit weiteren Anweisungen wurde gesendet.');
+                    showLogin();
+                } else {
+                    alert('Fehler beim Senden der E-Mail. Bitte überprüfe deine E-Mail Adresse.');
+                }
+            } catch (error) {
+                alert('Netzwerkfehler. Bitte versuche es später erneut.');
+            }
+        });
+
+        // Validation Functions
+        function validateRegistration() {
+            const password = document.getElementById('regPassword').value;
+            const passwordConfirm = document.getElementById('regPasswordConfirm').value;
+            
+            if (password !== passwordConfirm) {
+                showRegisterError('Passwörter stimmen nicht überein');
+                return false;
+            }
+            
+            if (password.length < 8) {
+                showRegisterError('Passwort muss mindestens 8 Zeichen lang sein');
+                return false;
+            }
+            
+            if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(password)) {
+                showRegisterError('Passwort muss Groß- und Kleinbuchstaben sowie Zahlen enthalten');
+                return false;
+            }
+            
+            return true;
+        }
+
+        function showLoginError(message) {
+            const errorDiv = document.getElementById('loginError');
+            const errorMessage = document.getElementById('errorMessage');
+            
+            errorMessage.textContent = message;
+            errorDiv.style.display = 'block';
+        }
+
+        function showRegisterError(message) {
+            const errorDiv = document.getElementById('registerError');
+            const errorMessage = document.getElementById('registerErrorMessage');
+            
+            errorMessage.textContent = message;
+            errorDiv.style.display = 'block';
+        }
+
+        function showRegisterSuccess() {
+            alert('Registrierung erfolgreich! Du kannst dich jetzt einloggen.');
+            showLogin();
+        }
+
+        // URL Parameter auswerten (für direkte Registrierung)
+        document.addEventListener('DOMContentLoaded', function() {
+            const urlParams = new URLSearchParams(window.location.search);
+            if (urlParams.get('register') === 'true') {
+                showRegister();
+            }
+        });
+    </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/athletik-webapp/src/main/webapp/stylesheet/athletik.css b/athletik-webapp/src/main/webapp/stylesheet/athletik.css
new file mode 100644 (file)
index 0000000..07a8a39
--- /dev/null
@@ -0,0 +1,124 @@
+@charset "UTF-8";
+
+BODY {
+       font-family: sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
+               'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
+               'Helvetica Neue';
+       line-height: 1.5;
+       font-size: 1rem;
+       margin: 0;
+}
+
+HEADER, SECTION {
+       margin-left: 15em;
+       padding: 0 0 0 1rem;
+}
+
+NAV {
+       background: #f0f0f0;
+       height: 100%;
+       position: fixed;
+       z-index: 1;
+       top: 0;
+       left: 0;
+       width: 15em;
+       overflow-x: hidden;
+}
+
+OL {
+       margin: 1.25em   0;
+}
+
+UL.navbar {
+       list-style-type: none;
+}
+
+.navbar li a {
+       text-decoration: none;
+       color: #333;
+       padding: 0.5rem 1rem;
+       display: block;
+       font-size: 2em;
+}
+
+.navbar li a:active, .navbar li a:hover {
+       background: #ddd;
+       transition: background 0.2s ease;
+}
+
+P.muscle {
+       border-top: 0.2em solid black;
+       padding: 0.25em 0 0 0;
+}
+
+P.start {
+       font-size: 1.25rem;
+       padding: 0 0 1.5em 0;
+}
+
+LI, P {
+       font-size: 1rem;
+}
+
+A {
+       text-decoration: none;
+}
+
+.musclename {
+       font-weight: 600;
+       color: #933;
+       background-color: #ddd;
+       padding: 0.5em;
+}
+
+.important {
+       font-weight: 600;
+       color: red;
+}
+
+.fa {
+       font-size: 1.5em;
+}
+
+H1 {
+       text-align: center;
+       font-size: 4.5em;
+}
+
+H2 {
+       text-align: center;
+       font-size: 3em;
+       text-decoration: underline overline;
+}
+
+.totop {
+       background-color: #666;
+       color: #ccc;
+       padding: 0.5em 2em;
+}
+
+.totop A {
+       color: #ccc;
+       font-size: 0.75em;
+}
+
+.hidden { display: none; }
+.success { 
+    background: #d4edda; 
+    color: #155724; 
+    padding: 10px; 
+    margin: 10px 0; 
+    border: 1px solid #c3e6cb;
+}
+.error { 
+    background: #f8d7da; 
+    color: #721c24; 
+    padding: 10px; 
+    margin: 10px 0; 
+    border: 1px solid #f5c6cb;
+}
+
+button:disabled {
+    opacity: 0.6;
+    cursor: not-allowed;
+}
\ No newline at end of file
diff --git a/athletik-webapp/src/main/webapp/training.html b/athletik-webapp/src/main/webapp/training.html
new file mode 100644 (file)
index 0000000..71273b6
--- /dev/null
@@ -0,0 +1,92 @@
+/* Zusätzliche Styles für die Training-Seite */
+
+/* Responsive Design */
+@media (max-width: 768px) {
+    .training-container {
+        padding: 10px;
+    }
+    
+    .stats-card {
+        margin-bottom: 15px;
+    }
+    
+    .exercise-card {
+        margin-bottom: 20px;
+    }
+}
+
+/* Animationen */
+@keyframes fadeIn {
+    from { opacity: 0; transform: translateY(20px); }
+    to { opacity: 1; transform: translateY(0); }
+}
+
+.exercise-card {
+    animation: fadeIn 0.5s ease;
+}
+
+/* Progress Bars */
+.progress {
+    height: 8px;
+    border-radius: 4px;
+}
+
+.progress-bar {
+    transition: width 0.3s ease;
+}
+
+/* Custom Scrollbar */
+.training-history::-webkit-scrollbar {
+    width: 6px;
+}
+
+.training-history::-webkit-scrollbar-track {
+    background: #f1f1f1;
+    border-radius: 3px;
+}
+
+.training-history::-webkit-scrollbar-thumb {
+    background: #888;
+    border-radius: 3px;
+}
+
+.training-history::-webkit-scrollbar-thumb:hover {
+    background: #555;
+}
+
+/* Hover Effects */
+.btn:hover {
+    transform: translateY(-1px);
+    transition: transform 0.2s ease;
+}
+
+/* Loading States */
+.loading {
+    opacity: 0.6;
+    pointer-events: none;
+}
+
+/* Notification System */
+.notification {
+    position: fixed;
+    top: 20px;
+    right: 20px;
+    padding: 15px 20px;
+    border-radius: 8px;
+    color: white;
+    z-index: 1000;
+    animation: slideIn 0.3s ease;
+}
+
+.notification.success {
+    background: #28a745;
+}
+
+.notification.error {
+    background: #dc3545;
+}
+
+@keyframes slideIn {
+    from { transform: translateX(100%); opacity: 0; }
+    to { transform: translateX(0); opacity: 1; }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 6e97c8969839ca77e94799e02ecbf23cd2728c39..10ff5216f1f3856094cefff9bffe39f371605af6 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -1,4 +1,5 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.triathlon-coaching.product</groupId>
@@ -6,14 +7,76 @@
        <version>0.0.1-SNAPSHOT</version>
        <packaging>pom</packaging>
 
-       <name>${product.artifactId}</name>
+       <name>${project.artifactId}</name>
        <description>Athletik Training</description>
 
        <modules>
                <module>athletik-web</module>
                <module>athletik-db</module>
+               <module>athletik-service</module>
+               <module>athletik-webapp</module>
        </modules>
 
+       <properties>
+               <java.version>17</java.version>
+
+               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+               <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+<!-- Plugin Versions-->
+               <maven.compiler.source>${java.version}</maven.compiler.source>
+               <maven.compiler.target>${java.version}</maven.compiler.target>
+
+               <maven-enforcer-plugin.version>3.3.0</maven-enforcer-plugin.version>
+
+               <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
+               <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
+               <maven-surefire-plugin.version>3.0.0</maven-surefire-plugin.version>
+
+               <maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
+               <maven-war-plugin.version>3.3.2</maven-war-plugin.version>
+               <maven-ear-plugin.version>3.3.0</maven-ear-plugin.version>
+               <maven-assembly-plugin.version>3.5.0</maven-assembly-plugin.version>
+               <maven-dependency-plugin.version>3.5.0</maven-dependency-plugin.version>
+
+               <maven-release-plugin.version>3.0.0</maven-release-plugin.version>
+               <maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
+               <maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version>
+               <maven-site-plugin.version>4.0.0-M6</maven-site-plugin.version>
+
+               <!-- Versions -->
+
+               <junit.version>4.13.2</junit.version>
+
+               <tomcat7-maven-plugin.version>2.2</tomcat7-maven-plugin.version>
+               <utillib.version>2.1.0</utillib.version>
+
+
+               <spring-boot.version>2.7.0</spring-boot.version>
+               <hibernate.version>5.6.9.Final</hibernate.version>
+               <postgresql.version>42.3.6</postgresql.version>
+               <thymeleaf.version>3.0.15.RELEASE</thymeleaf.version>
+               <hibernate-core.version>5.6.0.Final</hibernate-core.version>
+               <javax.mail.version>1.6.2</javax.mail.version>
+               <springframework.version>5.3.16</springframework.version>
+               <lombok.version>1.18.40</lombok.version>
+               <javax.servlet-api.version>4.0.1</javax.servlet-api.version>
+
+               <javadoc.opts>-Xdoclint:none</javadoc.opts>
+
+       </properties>
+
+       <dependencyManagement>
+               <dependencies>
+
+               </dependencies>
+       </dependencyManagement>
+
+       <dependencies>
+
+       </dependencies>
+
+
        <scm>
                <connection>scm:git:ssh://laktatnebel.de/srv/git/swim.git</connection>
                <developerConnection>scm:git:ssh://laktatnebel.de/srv/git/swim.git</developerConnection>