From 7ae3dbd27f14c06503cafdaad9eb803fff21a2b5 Mon Sep 17 00:00:00 2001 From: Will Baumbach Date: Thu, 26 Mar 2026 16:39:30 -0500 Subject: [PATCH] adding example data for pfits 18/55 and hovering over cells. Next up is clicking to change color to green and right clicking to reset to white. --- src/app/app.component.html | 337 +------ src/app/app.component.ts | 3 +- src/app/calendar/calendar.component.html | 30 + src/app/calendar/calendar.component.scss | 39 + src/app/calendar/calendar.component.spec.ts | 23 + src/app/calendar/calendar.component.ts | 18 + src/app/models/plan.model.ts | 10 + src/app/models/workout.model.ts | 20 + src/app/testData/examplePlan.ts | 930 ++++++++++++++++++++ 9 files changed, 1073 insertions(+), 337 deletions(-) create mode 100644 src/app/calendar/calendar.component.html create mode 100644 src/app/calendar/calendar.component.scss create mode 100644 src/app/calendar/calendar.component.spec.ts create mode 100644 src/app/calendar/calendar.component.ts create mode 100644 src/app/models/plan.model.ts create mode 100644 src/app/models/workout.model.ts create mode 100644 src/app/testData/examplePlan.ts diff --git a/src/app/app.component.html b/src/app/app.component.html index 36093e1..68e3b6a 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,336 +1 @@ - - - - - - - - - - - -
-
-
- -

Hello, {{ title }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
-
-
- - - - - - - - - - - + \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index ad0e221..b7dbc73 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,10 +1,11 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { CalendarComponent } from "./calendar/calendar.component"; @Component({ selector: 'app-root', standalone: true, - imports: [RouterOutlet], + imports: [RouterOutlet, CalendarComponent], templateUrl: './app.component.html', styleUrl: './app.component.scss' }) diff --git a/src/app/calendar/calendar.component.html b/src/app/calendar/calendar.component.html new file mode 100644 index 0000000..cf80197 --- /dev/null +++ b/src/app/calendar/calendar.component.html @@ -0,0 +1,30 @@ +
+ + + + + + + + + + + + + @for (week of examplePlan.workouts; track $index) { + + + @for (day of week; track $index) { + + } + + + } +
WeekMondayTuesdayWednesdayThursdayFridaySaturdaySundayTotal Milage
+
Week {{$index + 1}}
+
+
{{day.type}}
+
{{day.totalDistance == 0 ? '' : day.totalDistance + ' miles'}}
+
{{day.description}}
+
43
+
\ No newline at end of file diff --git a/src/app/calendar/calendar.component.scss b/src/app/calendar/calendar.component.scss new file mode 100644 index 0000000..436e9d3 --- /dev/null +++ b/src/app/calendar/calendar.component.scss @@ -0,0 +1,39 @@ +table { + width: 100%; +} + +tr { + display: flex; + width: 100%; +} + +td { + padding: 20px 0; +} + +th, +td { + flex: 1; + text-align: center; + border: 1px solid black; +} + +.container { + margin-left: 24px; + margin-right: 24px; +} + +.noselect { +-webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Old versions of Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently + supported by Chrome, Edge, Opera and Firefox */ +} + +.hoverOverWorkout:hover { + background-color: rgb(211, 211, 211); +} + diff --git a/src/app/calendar/calendar.component.spec.ts b/src/app/calendar/calendar.component.spec.ts new file mode 100644 index 0000000..9e08dee --- /dev/null +++ b/src/app/calendar/calendar.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CalendarComponent } from './calendar.component'; + +describe('CalendarComponent', () => { + let component: CalendarComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CalendarComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CalendarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/calendar/calendar.component.ts b/src/app/calendar/calendar.component.ts new file mode 100644 index 0000000..38ee5cb --- /dev/null +++ b/src/app/calendar/calendar.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; +import { examplePlan } from '../testData/examplePlan'; +import { Plan } from '../models/plan.model'; + +@Component({ + selector: 'calendar', + standalone: true, + imports: [], + templateUrl: './calendar.component.html', + styleUrl: './calendar.component.scss' +}) +export class CalendarComponent { + examplePlan: Plan = examplePlan; + + ngOnInit(): void { + + } +} diff --git a/src/app/models/plan.model.ts b/src/app/models/plan.model.ts new file mode 100644 index 0000000..4d9b3e0 --- /dev/null +++ b/src/app/models/plan.model.ts @@ -0,0 +1,10 @@ +import { Workout } from "./workout.model"; + +export interface Plan { + name: string, + description: string, + startDate: Date, + endDate: Date, + numWeeks: number, + workouts: Workout[][] +} \ No newline at end of file diff --git a/src/app/models/workout.model.ts b/src/app/models/workout.model.ts new file mode 100644 index 0000000..d8aa871 --- /dev/null +++ b/src/app/models/workout.model.ts @@ -0,0 +1,20 @@ +export interface Workout { + week: number + day: number, + type: WorkoutType, + totalDistance: number, + description: string +} + +export enum WorkoutType { + VO2Max = "VO2 Max", + DressRehersal = "Dress Rehersal", + RaceDay = "Race Day", + MarathonPaceLongRun = "MP Long Run", + LongRun = "Long Run", + MediumLongRun = "Medium-Long Run", + Threshold = "Threshold", + GeneralAerobic = "General Aerobic", + Recovery = "Recovery", + Rest = "Rest or Crosstrain" +} \ No newline at end of file diff --git a/src/app/testData/examplePlan.ts b/src/app/testData/examplePlan.ts new file mode 100644 index 0000000..94de96c --- /dev/null +++ b/src/app/testData/examplePlan.ts @@ -0,0 +1,930 @@ +import { Plan } from "../models/plan.model"; +import { Workout, WorkoutType } from "../models/workout.model"; + +export const examplePlan: Plan = { + name: 'Pfitz 18/55', + description: '18 Week marathon program peaking at around 55 miles per week', + startDate: new Date('2026-03-23'), + endDate: new Date('2026-03-23'), + numWeeks: 18, + workouts: [ + [ + { + week: 1, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 1, + day: 1, + type: WorkoutType.Threshold, + totalDistance: 8, + description: '20-25 min LT' + }, + { + week: 1, + day: 2, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 1, + day: 3, + type: WorkoutType.GeneralAerobic, + totalDistance: 8, + description: '' + }, + { + week: 1, + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 1, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 1, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 11, + description: '' + } + ], + [ + { + week: 2, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 2, + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 8, + description: '6 x 10s Hills, 8 x 100m strides' + }, + { + week: 2, + day: 2, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 2, + day: 3, + type: WorkoutType.GeneralAerobic, + totalDistance: 9, + description: '' + }, + { + week: 2, + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 2, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 2, + day: 6, + type: WorkoutType.MarathonPaceLongRun, + totalDistance: 12, + description: '8 mi @ marathon pace' + } + ], + [ + { + week: 3, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 3, + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 9, + description: '' + }, + { + week: 3, + day: 2, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 3, + day: 3, + type: WorkoutType.Threshold, + totalDistance: 8, + description: '25-30 min LT' + }, + { + week: 3, + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 3, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 3, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 14, + description: '' + } + ], + [ + { + week: 4, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 4, + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 8, + description: '6 x 10s Hills, 8 x 100m strides' + }, + { + week: 4, + day: 2, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 4, + day: 3, + type: WorkoutType.GeneralAerobic, + totalDistance: 9, + description: '' + }, + { + week: 4, + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 4, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 4, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 15, + description: '' + } + ], + [ + { + week: 5, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 5, + day: 1, + type: WorkoutType.Threshold, + totalDistance: 9, + description: '30-35 min LT' + }, + { + week: 5, + day: 2, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '' + }, + { + week: 5, + day: 3, + type: WorkoutType.GeneralAerobic, + totalDistance: 9, + description: '' + }, + { + week: 5, + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 5, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 5, + day: 6, + type: WorkoutType.MarathonPaceLongRun, + totalDistance: 15, + description: '10 mi @ marathon pace' + } + ], + [ + { + week: 6, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 6, + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 7, + description: '10 x 100m strides' + }, + { + week: 6, + day: 2, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 6, + day: 3, + type: WorkoutType.GeneralAerobic, + totalDistance: 7, + description: '' + }, + { + week: 6, + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 6, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 6, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 11, + description: '' + } + ], + [ + { + week: 7, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 7, + day: 1, + type: WorkoutType.Threshold, + totalDistance: 9, + description: '30-35 min LT' + }, + { + week: 7, + day: 2, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 7, + day: 3, + type: WorkoutType.MediumLongRun, + totalDistance: 11, + description: '' + }, + { + week: 7, + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 7, + day: 5, + type: WorkoutType.GeneralAerobic, + totalDistance: 7, + description: '6 x 10s Hills, 8 x 100m strides' + }, + { + week: 7, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 17, + description: '' + } + ], + [ + { + week: 8, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 8, + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '6x100 strides' + }, + { + week: 8, + day: 2, + type: WorkoutType.MediumLongRun, + totalDistance: 12, + description: '' + }, + { + week: 8, + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 8, + day: 4, + type: WorkoutType.Threshold, + totalDistance: 10, + description: '35-40 min LT' + }, + { + week: 8, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 8, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 19, + description: '' + } + ], + [ + { + week: 9, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 9, + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '' + }, + { + week: 9, + day: 2, + type: WorkoutType.MediumLongRun, + totalDistance: 13, + description: '' + }, + { + week: 9, + day: 3, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '' + }, + { + week: 9, + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 9, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '6x100 strides' + }, + { + week: 9, + day: 6, + type: WorkoutType.MarathonPaceLongRun, + totalDistance: 16, + description: '12 mi @ marathon pace' + } + ], + [ + { + week: 10, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 10, + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 8, + description: '' + }, + { + week: 10, + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 8, + description: '6 x 600m @ 5K pace' + }, + { + week: 10, + day: 3, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 10, + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 10, + day: 5, + type: WorkoutType.GeneralAerobic, + totalDistance: 7, + description: '6 x 10s Hills, 8 x 100m strides' + }, + { + week: 10, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 13, + description: '' + } + ], + [ + { + week: 11, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 11, + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '6x100 strides' + }, + { + week: 11, + day: 2, + type: WorkoutType.Threshold, + totalDistance: 11, + description: '35-45 min LT' + }, + { + week: 11, + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 11, + day: 4, + type: WorkoutType.MediumLongRun, + totalDistance: 12, + description: '' + }, + { + week: 11, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 11, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 19, + description: '' + } + ], + [ + { + week: 12, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 12, + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 10, + description: '' + }, + { + week: 12, + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 8, + description: '5 x 600m @ 5K pace' + }, + { + week: 12, + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 12, + day: 4, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '6x100 strides' + }, + { + week: 12, + day: 5, + type: WorkoutType.VO2Max, + totalDistance: 10, + description: '8K-15K Tune Up' + }, + { + week: 12, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 16, + description: '' + } + ], + [ + { + week: 13, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 13, + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '' + }, + { + week: 13, + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 9, + description: '5 x 1000m @ 5K pace' + }, + { + week: 13, + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 13, + day: 4, + type: WorkoutType.MediumLongRun, + totalDistance: 11, + description: '' + }, + { + week: 13, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '' + }, + { + week: 13, + day: 6, + type: WorkoutType.MarathonPaceLongRun, + totalDistance: 17, + description: '14 mi @ marathon pace' + } + ], + [ + { + week: 14, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 14, + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 10, + description: '' + }, + { + week: 14, + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 8, + description: '5 x 600m @ 5K pace' + }, + { + week: 14, + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 14, + day: 4, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '6x100 strides' + }, + { + week: 14, + day: 5, + type: WorkoutType.VO2Max, + totalDistance: 10, + description: '8K-15K Tune Up' + }, + { + week: 14, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 16, + description: '' + } + ], + [ + { + week: 15, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 15, + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '' + }, + { + week: 15, + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 9, + description: '5 x 1000m @ 5K pace' + }, + { + week: 15, + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 15, + day: 4, + type: WorkoutType.MediumLongRun, + totalDistance: 11, + description: '' + }, + { + week: 15, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + week: 15, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 19, + description: '' + } + ], + [ + { + week: 16, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 16, + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 7, + description: '6 x 10s Hills, 10 x 100m strides' + }, + { + week: 16, + day: 2, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '' + }, + { + week: 16, + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 16, + day: 4, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '4-5 mi w/ 6x100 strides' + }, + { + week: 16, + day: 5, + type: WorkoutType.VO2Max, + totalDistance: 9, + description: '8K-10K Tune Up' + }, + { + week: 16, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 16, + description: '' + } + ], + [ + { + week: 17, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 17, + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '8x100 strides' + }, + { + week: 17, + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 8, + description: '5 x 1000m @ 5K pace' + }, + { + week: 17, + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 17, + day: 4, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '6x100 strides' + }, + { + week: 17, + day: 5, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 17, + day: 6, + type: WorkoutType.LongRun, + totalDistance: 12, + description: '' + } + ], + [ + { + week: 18, + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 18, + day: 1, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '8x100 strides' + }, + { + week: 18, + day: 2, + type: WorkoutType.DressRehersal, + totalDistance: 5, + description: '2 mi @ marathon pace' + }, + { + week: 18, + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + week: 18, + day: 4, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '6x100 strides' + }, + { + week: 18, + day: 5, + type: WorkoutType.Recovery, + totalDistance: 2, + description: '' + }, + { + week: 18, + day: 6, + type: WorkoutType.RaceDay, + totalDistance: 26.2, + description: '' + } + ], + ] +}; \ No newline at end of file