From 7ae3dbd27f14c06503cafdaad9eb803fff21a2b5 Mon Sep 17 00:00:00 2001 From: Will Baumbach Date: Thu, 26 Mar 2026 16:39:30 -0500 Subject: [PATCH 1/7] 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 -- 2.49.1 From 349ac48fa23b5e6ef9c4c9879627b7f352f1f30a Mon Sep 17 00:00:00 2001 From: Will Baumbach Date: Thu, 26 Mar 2026 16:49:50 -0500 Subject: [PATCH 2/7] Refactoring data model --- src/app/calendar/calendar.component.html | 12 +- src/app/models/plan.model.ts | 7 +- src/app/models/workout.model.ts | 1 - src/app/testData/examplePlan.ts | 1768 +++++++++++----------- 4 files changed, 860 insertions(+), 928 deletions(-) diff --git a/src/app/calendar/calendar.component.html b/src/app/calendar/calendar.component.html index cf80197..8986a78 100644 --- a/src/app/calendar/calendar.component.html +++ b/src/app/calendar/calendar.component.html @@ -11,16 +11,16 @@ Sunday Total Milage - @for (week of examplePlan.workouts; track $index) { + @for (planWeek of examplePlan.planDetials; track $index) { -
Week {{$index + 1}}
+
Week {{planWeek.week}}
- @for (day of week; track $index) { + @for (workout of planWeek.workouts; track $index) { -
{{day.type}}
-
{{day.totalDistance == 0 ? '' : day.totalDistance + ' miles'}}
-
{{day.description}}
+
{{workout.type}}
+
{{workout.totalDistance == 0 ? '' : workout.totalDistance + ' miles'}}
+
{{workout.description}}
} 43 diff --git a/src/app/models/plan.model.ts b/src/app/models/plan.model.ts index 4d9b3e0..b083185 100644 --- a/src/app/models/plan.model.ts +++ b/src/app/models/plan.model.ts @@ -6,5 +6,10 @@ export interface Plan { startDate: Date, endDate: Date, numWeeks: number, - workouts: Workout[][] + planDetials: Week[] +} + +export interface Week { + week: 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 index d8aa871..91c1a2d 100644 --- a/src/app/models/workout.model.ts +++ b/src/app/models/workout.model.ts @@ -1,5 +1,4 @@ export interface Workout { - week: number day: number, type: WorkoutType, totalDistance: number, diff --git a/src/app/testData/examplePlan.ts b/src/app/testData/examplePlan.ts index 94de96c..fc45010 100644 --- a/src/app/testData/examplePlan.ts +++ b/src/app/testData/examplePlan.ts @@ -7,924 +7,852 @@ export const examplePlan: Plan = { 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: '' - } - ], + planDetials: [ + { + week: 1, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.Threshold, + totalDistance: 8, + description: '20-25 min LT' + }, + { + day: 2, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 3, + type: WorkoutType.GeneralAerobic, + totalDistance: 8, + description: '' + }, + { + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 11, + description: '' + } + ] + }, + { + week: 2, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 8, + description: '6 x 10s Hills, 8 x 100m strides' + }, + { + day: 2, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 3, + type: WorkoutType.GeneralAerobic, + totalDistance: 9, + description: '' + }, + { + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 6, + type: WorkoutType.MarathonPaceLongRun, + totalDistance: 12, + description: '8 mi @ marathon pace' + } + ] + }, + { + week: 3, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 9, + description: '' + }, + { + day: 2, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 3, + type: WorkoutType.Threshold, + totalDistance: 8, + description: '25-30 min LT' + }, + { + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 14, + description: '' + } + ] + }, + { + week: 4, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 8, + description: '6 x 10s Hills, 8 x 100m strides' + }, + { + day: 2, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 3, + type: WorkoutType.GeneralAerobic, + totalDistance: 9, + description: '' + }, + { + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 15, + description: '' + } + ] + }, + { + week: 5, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.Threshold, + totalDistance: 9, + description: '30-35 min LT' + }, + { + day: 2, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '' + }, + { + day: 3, + type: WorkoutType.GeneralAerobic, + totalDistance: 9, + description: '' + }, + { + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 6, + type: WorkoutType.MarathonPaceLongRun, + totalDistance: 15, + description: '10 mi @ marathon pace' + } + ] + }, + { + week: 6, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 7, + description: '10 x 100m strides' + }, + { + day: 2, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 3, + type: WorkoutType.GeneralAerobic, + totalDistance: 7, + description: '' + }, + { + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 11, + description: '' + } + ] + }, + { + week: 7, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.Threshold, + totalDistance: 9, + description: '30-35 min LT' + }, + { + day: 2, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 3, + type: WorkoutType.MediumLongRun, + totalDistance: 11, + description: '' + }, + { + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 5, + type: WorkoutType.GeneralAerobic, + totalDistance: 7, + description: '6 x 10s Hills, 8 x 100m strides' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 17, + description: '' + } + ] + }, + { + week: 8, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '6x100 strides' + }, + { + day: 2, + type: WorkoutType.MediumLongRun, + totalDistance: 12, + description: '' + }, + { + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 4, + type: WorkoutType.Threshold, + totalDistance: 10, + description: '35-40 min LT' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 19, + description: '' + } + ] + }, + { + week: 9, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '' + }, + { + day: 2, + type: WorkoutType.MediumLongRun, + totalDistance: 13, + description: '' + }, + { + day: 3, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '' + }, + { + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '6x100 strides' + }, + { + day: 6, + type: WorkoutType.MarathonPaceLongRun, + totalDistance: 16, + description: '12 mi @ marathon pace' + } + ] + }, + { + week: 10, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 8, + description: '' + }, + { + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 8, + description: '6 x 600m @ 5K pace' + }, + { + day: 3, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 4, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 5, + type: WorkoutType.GeneralAerobic, + totalDistance: 7, + description: '6 x 10s Hills, 8 x 100m strides' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 13, + description: '' + } + ] + }, + { + week: 11, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '6x100 strides' + }, + { + day: 2, + type: WorkoutType.Threshold, + totalDistance: 11, + description: '35-45 min LT' + }, + { + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 4, + type: WorkoutType.MediumLongRun, + totalDistance: 12, + description: '' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 19, + description: '' + } + ] + }, + { + week: 12, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 10, + description: '' + }, + { + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 8, + description: '5 x 600m @ 5K pace' + }, + { + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 4, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '6x100 strides' + }, + { + day: 5, + type: WorkoutType.VO2Max, + totalDistance: 10, + description: '8K-15K Tune Up' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 16, + description: '' + } + ] + }, + { + week: 13, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '' + }, + { + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 9, + description: '5 x 1000m @ 5K pace' + }, + { + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 4, + type: WorkoutType.MediumLongRun, + totalDistance: 11, + description: '' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '' + }, + { + day: 6, + type: WorkoutType.MarathonPaceLongRun, + totalDistance: 17, + description: '14 mi @ marathon pace' + } + ] + }, + { + week: 14, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 10, + description: '' + }, + { + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 8, + description: '5 x 600m @ 5K pace' + }, + { + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 4, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '6x100 strides' + }, + { + day: 5, + type: WorkoutType.VO2Max, + totalDistance: 10, + description: '8K-15K Tune Up' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 16, + description: '' + } + ] + }, + { + week: 15, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '' + }, + { + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 9, + description: '5 x 1000m @ 5K pace' + }, + { + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 4, + type: WorkoutType.MediumLongRun, + totalDistance: 11, + description: '' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 19, + description: '' + } + ] + }, + { + week: 16, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.GeneralAerobic, + totalDistance: 7, + description: '6 x 10s Hills, 10 x 100m strides' + }, + { + day: 2, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '' + }, + { + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 4, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '4-5 mi w/ 6x100 strides' + }, + { + day: 5, + type: WorkoutType.VO2Max, + totalDistance: 9, + description: '8K-10K Tune Up' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 16, + description: '' + } + ] + }, + { + week: 17, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.Recovery, + totalDistance: 6, + description: '8x100 strides' + }, + { + day: 2, + type: WorkoutType.VO2Max, + totalDistance: 8, + description: '5 x 1000m @ 5K pace' + }, + { + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 4, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '6x100 strides' + }, + { + day: 5, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 6, + type: WorkoutType.LongRun, + totalDistance: 12, + description: '' + } + ] + }, + { + week: 18, + workouts: [ + { + day: 0, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 1, + type: WorkoutType.Recovery, + totalDistance: 5, + description: '8x100 strides' + }, + { + day: 2, + type: WorkoutType.DressRehersal, + totalDistance: 5, + description: '2 mi @ marathon pace' + }, + { + day: 3, + type: WorkoutType.Rest, + totalDistance: 0, + description: '' + }, + { + day: 4, + type: WorkoutType.Recovery, + totalDistance: 4, + description: '6x100 strides' + }, + { + day: 5, + type: WorkoutType.Recovery, + totalDistance: 2, + description: '' + }, + { + day: 6, + type: WorkoutType.RaceDay, + totalDistance: 26.2, + description: '' + } + ] + } ] -}; \ No newline at end of file +}; -- 2.49.1 From 1eb77cd6b6769419eab454722f05f85f88113ef4 Mon Sep 17 00:00:00 2001 From: Will Baumbach Date: Fri, 17 Apr 2026 12:09:32 -0500 Subject: [PATCH 3/7] adding complete and undo-complete on click --- src/app/calendar/calendar.component.html | 4 ++-- src/app/calendar/calendar.component.scss | 10 ++++++++-- src/app/calendar/calendar.component.ts | 9 +++++++++ src/app/models/plan.model.ts | 1 + src/app/testData/examplePlan.ts | 18 ++++++++++++++++++ 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/app/calendar/calendar.component.html b/src/app/calendar/calendar.component.html index 8986a78..4f9c5f9 100644 --- a/src/app/calendar/calendar.component.html +++ b/src/app/calendar/calendar.component.html @@ -17,13 +17,13 @@
Week {{planWeek.week}}
@for (workout of planWeek.workouts; track $index) { - +
{{workout.type}}
{{workout.totalDistance == 0 ? '' : workout.totalDistance + ' miles'}}
{{workout.description}}
} - 43 + {{planWeek.totalMilage}} } diff --git a/src/app/calendar/calendar.component.scss b/src/app/calendar/calendar.component.scss index 436e9d3..fb1566a 100644 --- a/src/app/calendar/calendar.component.scss +++ b/src/app/calendar/calendar.component.scss @@ -33,7 +33,13 @@ td { supported by Chrome, Edge, Opera and Firefox */ } -.hoverOverWorkout:hover { - background-color: rgb(211, 211, 211); +.incompleteWorkout:hover { + background-color: rgb(235, 235, 235); } +.completedWorkout { + background-color: rgb(107, 194, 107); +} +.completedWorkout:hover { + background-color: rgb(89, 161, 89); +} \ No newline at end of file diff --git a/src/app/calendar/calendar.component.ts b/src/app/calendar/calendar.component.ts index 38ee5cb..924e894 100644 --- a/src/app/calendar/calendar.component.ts +++ b/src/app/calendar/calendar.component.ts @@ -15,4 +15,13 @@ export class CalendarComponent { ngOnInit(): void { } + + onCellClick(event: Event) { + const target = event.currentTarget as HTMLElement; + if (target.classList.contains('incompleteWorkout')) { + target.classList.replace('incompleteWorkout', 'completedWorkout') + } else if (target.classList.contains('completedWorkout')) { + target.classList.replace('completedWorkout', 'incompleteWorkout') + } + } } diff --git a/src/app/models/plan.model.ts b/src/app/models/plan.model.ts index b083185..73743d8 100644 --- a/src/app/models/plan.model.ts +++ b/src/app/models/plan.model.ts @@ -11,5 +11,6 @@ export interface Plan { export interface Week { week: number, + totalMilage: number, workouts: Workout[] } \ No newline at end of file diff --git a/src/app/testData/examplePlan.ts b/src/app/testData/examplePlan.ts index fc45010..2428740 100644 --- a/src/app/testData/examplePlan.ts +++ b/src/app/testData/examplePlan.ts @@ -10,6 +10,7 @@ export const examplePlan: Plan = { planDetials: [ { week: 1, + totalMilage: 31, workouts: [ { day: 0, @@ -57,6 +58,7 @@ export const examplePlan: Plan = { }, { week: 2, + totalMilage: 33, workouts: [ { day: 0, @@ -104,6 +106,7 @@ export const examplePlan: Plan = { }, { week: 3, + totalMilage: 39, workouts: [ { day: 0, @@ -151,6 +154,7 @@ export const examplePlan: Plan = { }, { week: 4, + totalMilage: 40, workouts: [ { day: 0, @@ -198,6 +202,7 @@ export const examplePlan: Plan = { }, { week: 5, + totalMilage: 42, workouts: [ { day: 0, @@ -245,6 +250,7 @@ export const examplePlan: Plan = { }, { week: 6, + totalMilage: 33, workouts: [ { day: 0, @@ -292,6 +298,7 @@ export const examplePlan: Plan = { }, { week: 7, + totalMilage: 48, workouts: [ { day: 0, @@ -339,6 +346,7 @@ export const examplePlan: Plan = { }, { week: 8, + totalMilage: 51, workouts: [ { day: 0, @@ -386,6 +394,7 @@ export const examplePlan: Plan = { }, { week: 9, + totalMilage: 46, workouts: [ { day: 0, @@ -433,6 +442,7 @@ export const examplePlan: Plan = { }, { week: 10, + totalMilage: 40, workouts: [ { day: 0, @@ -480,6 +490,7 @@ export const examplePlan: Plan = { }, { week: 11, + totalMilage: 52, workouts: [ { day: 0, @@ -527,6 +538,7 @@ export const examplePlan: Plan = { }, { week: 12, + totalMilage: 48, workouts: [ { day: 0, @@ -574,6 +586,7 @@ export const examplePlan: Plan = { }, { week: 13, + totalMilage: 48, workouts: [ { day: 0, @@ -621,6 +634,7 @@ export const examplePlan: Plan = { }, { week: 14, + totalMilage: 48, workouts: [ { day: 0, @@ -668,6 +682,7 @@ export const examplePlan: Plan = { }, { week: 15, + totalMilage: 49, workouts: [ { day: 0, @@ -715,6 +730,7 @@ export const examplePlan: Plan = { }, { week: 16, + totalMilage: 41, workouts: [ { day: 0, @@ -762,6 +778,7 @@ export const examplePlan: Plan = { }, { week: 17, + totalMilage: 31, workouts: [ { day: 0, @@ -809,6 +826,7 @@ export const examplePlan: Plan = { }, { week: 18, + totalMilage: 16, workouts: [ { day: 0, -- 2.49.1 From 3744423e5f408114022a6f79b45551e8c641a8f0 Mon Sep 17 00:00:00 2001 From: Will Baumbach Date: Wed, 22 Apr 2026 10:33:03 -0500 Subject: [PATCH 4/7] generated backend with express --- .gitignore | 1 + backend/.env | 0 backend/app.js | 41 + backend/bin/www | 90 +++ backend/package-lock.json | 1045 ++++++++++++++++++++++++++ backend/package.json | 16 + backend/public/stylesheets/style.css | 8 + backend/routes/index.js | 9 + backend/routes/users.js | 9 + backend/views/error.jade | 6 + backend/views/index.jade | 5 + backend/views/layout.jade | 7 + 12 files changed, 1237 insertions(+) create mode 100644 backend/.env create mode 100644 backend/app.js create mode 100644 backend/bin/www create mode 100644 backend/package-lock.json create mode 100644 backend/package.json create mode 100644 backend/public/stylesheets/style.css create mode 100644 backend/routes/index.js create mode 100644 backend/routes/users.js create mode 100644 backend/views/error.jade create mode 100644 backend/views/index.jade create mode 100644 backend/views/layout.jade diff --git a/.gitignore b/.gitignore index cc7b141..04bc98e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ # Node /node_modules +/backend/node_modules npm-debug.log yarn-error.log diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..e69de29 diff --git a/backend/app.js b/backend/app.js new file mode 100644 index 0000000..662bcc9 --- /dev/null +++ b/backend/app.js @@ -0,0 +1,41 @@ +var createError = require('http-errors'); +var express = require('express'); +var path = require('path'); +var cookieParser = require('cookie-parser'); +var logger = require('morgan'); + +var indexRouter = require('./routes/index'); +var usersRouter = require('./routes/users'); + +var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'jade'); + +app.use(logger('dev')); +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +app.use('/', indexRouter); +app.use('/users', usersRouter); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + next(createError(404)); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + res.render('error'); +}); + +module.exports = app; diff --git a/backend/bin/www b/backend/bin/www new file mode 100644 index 0000000..192c6f3 --- /dev/null +++ b/backend/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('backend:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..20f66c1 --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,1045 @@ +{ + "name": "backend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "backend", + "version": "0.0.0", + "dependencies": { + "cookie-parser": "~1.4.4", + "debug": "~2.6.9", + "express": "~4.16.1", + "http-errors": "~1.6.3", + "jade": "~1.11.0", + "morgan": "~1.9.1" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", + "integrity": "sha512-pXK8ez/pVjqFdAgBkF1YPVRacuLQ9EXBKaKWaeh58WNfMkCmZhOZzu+NtKSPD5PHmCCHheQ5cD29qM1K4QTxIg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz", + "integrity": "sha512-j3/4pkfih8W4NK22gxVSXcEonTpAHOHh0hu5BoZrKcOsW/4oBPxTi4Yk3SAj+FhC1f3+bRTkXdm4019gw1vg9g==", + "license": "MIT", + "dependencies": { + "acorn": "^2.1.0" + } + }, + "node_modules/align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==", + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "license": "BSD-3-Clause OR MIT", + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/asap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz", + "integrity": "sha512-Ej9qjcXY+8Tuy1cNqiwNMwFRXOy9UwgTeMA8LxreodygIPV48lx8PU1ecFxb5ZeU1DpMKxiq6vGLTxcitWZPbA==" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha512-YQyoqQG3sO8iCmf8+hyVpgHHOv0/hCEFiS4zTGUwTA1HjAFX66wRcNQrVCeJq9pgESMRvUAOvSil5MJlmccuKQ==", + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==", + "license": "MIT", + "dependencies": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/character-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.1.tgz", + "integrity": "sha512-6OEBVBlf/y8LaAphnbAnt743O3zMhlBer+FO5D40H6wqAdU9B1TvuApkejgLW0cvv0tEZNLktv1AnRI+C87ueQ==", + "license": "MIT" + }, + "node_modules/clean-css": { + "version": "3.4.28", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", + "integrity": "sha512-aTWyttSdI2mYi07kWqHi24NUU9YlELFKGOAgFzZjDN1064DMAOy2FBuoyGmkKRlXkbpXd0EVHmiVkbKhKoirTw==", + "license": "MIT", + "dependencies": { + "commander": "2.8.x", + "source-map": "0.4.x" + }, + "bin": { + "cleancss": "bin/cleancss" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-css/node_modules/commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha512-+pJLBFVk+9ZZdlAOB5WuIElVPPth47hILFkmGym57aq8kwxsowvByvB0DHs1vQAhyMZzdcpTtF0VDKGkSDR4ZQ==", + "license": "MIT", + "dependencies": { + "graceful-readlink": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==", + "license": "ISC", + "dependencies": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "node_modules/commander": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", + "integrity": "sha512-PhbTMT+ilDXZKqH8xbvuUY2ZEQNef0Q7DKxgoEKb4ccytsdvVVJmYqR0sGbi96nxU6oGrwEIQnclpK2NBZuQlg==", + "license": "MIT", + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/constantinople": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.0.2.tgz", + "integrity": "sha512-UnEggAQrmhxuTxlb7n1OsTtagNXWUv2CRlOogZhWOU4jLK4EJEbF8UDSNxuGu+jVtWNtO2j51ab2H1wlBIzF/w==", + "deprecated": "Please update to at least constantinople 3.1.1", + "license": "MIT", + "dependencies": { + "acorn": "^2.1.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/css": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz", + "integrity": "sha512-qmTYWhHk910nQWnGqMAiWWPQlB6tESiWgNebQJmiozOAGcBAQ1+U/UzUOkhdrcshlkSRRiKWodwmVvO0OmnIGg==", + "dependencies": { + "css-parse": "1.0.4", + "css-stringify": "1.0.5" + } + }, + "node_modules/css-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz", + "integrity": "sha512-pfstzKVRZiHprDXdsmtfH1HYUEw22lzjuHdnpe1hscwoQvgW2C5zDQIBE0RKoALEReTn9W1ECdY8uaT/kO4VfA==" + }, + "node_modules/css-stringify": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz", + "integrity": "sha512-aIThpcErhG5EyHorGqNlTh0TduNBqLrrXLO3x5rku3ZKBxuVfY+T7noyM2G2X/01iQANqJUb6d3+FLoa+N7Xwg==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, + "node_modules/jade": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/jade/-/jade-1.11.0.tgz", + "integrity": "sha512-J76sbGKeLtu7uwW97Ntzb1UvGnpKTDplYa9ROr2gNRhM+SxvlBSG0Ees3TQ8+7ya2UVkzMEeFxhRhEpN68s7Tg==", + "deprecated": "Jade has been renamed to pug, please install the latest version of pug instead of jade", + "license": "MIT", + "dependencies": { + "character-parser": "1.2.1", + "clean-css": "^3.1.9", + "commander": "~2.6.0", + "constantinople": "~3.0.1", + "jstransformer": "0.0.2", + "mkdirp": "~0.5.0", + "transformers": "2.1.0", + "uglify-js": "^2.4.19", + "void-elements": "~2.0.1", + "with": "~4.0.0" + }, + "bin": { + "jade": "bin/jade.js" + } + }, + "node_modules/jstransformer": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz", + "integrity": "sha512-b7tmf91j1ChMuYhwbPBnNgB62dmHuqiHpOdd6QLKzde8HydZqm+ud3qWreGWecSxPBFFNOf1Ozjx0xo2plFdHA==", + "license": "MIT", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^6.0.1" + } + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "license": "MIT", + "bin": { + "mime": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==", + "license": "MIT/X11", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "license": "MIT" + }, + "node_modules/promise": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", + "integrity": "sha512-O+uwGKreKNKkshzZv2P7N64lk6EP17iXBn0PbUnNQhk+Q0AHLstiTrjkx3v5YBd3cxUe7Sq6KyRhl/A0xUjk7Q==", + "license": "MIT", + "dependencies": { + "asap": "~1.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==", + "license": "MIT", + "dependencies": { + "align-text": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "license": "ISC" + }, + "node_modules/source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha512-Y8nIfcb1s/7DcobUz1yOO1GSp7gyL+D9zLHDehT7iRESqGSxjJ448Sg7rvfgsRJCnKLdSl11uGf0s9X80cH0/A==", + "license": "BSD-3-Clause", + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/transformers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz", + "integrity": "sha512-zJf5m2EIOngmBbDe2fhTPpCjzM2qkZVqrFJZc2jaln+KBeEaYKhS2QMOIkfVrNUyoOwqgbTwOHATzr3jZRQDyg==", + "deprecated": "Deprecated, use jstransformer", + "license": "MIT", + "dependencies": { + "css": "~1.0.8", + "promise": "~2.0", + "uglify-js": "~2.2.5" + } + }, + "node_modules/transformers/node_modules/is-promise": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", + "integrity": "sha512-mjWH5XxnhMA8cFnDchr6qRP9S/kLntKuEfIYku+PaN1CnS8v+OG9O/BKpRCVRJvpIkgAZm0Pf5Is3iSSOILlcg==", + "license": "MIT" + }, + "node_modules/transformers/node_modules/promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz", + "integrity": "sha512-OgMc+sxI3zWF8D5BJGtA0z7/IsrDy1/0cPaDv6HPpqa2fSTo7AdON5U10NbZCUeF+zCAj3PtfPE50Hf02386aA==", + "license": "MIT", + "dependencies": { + "is-promise": "~1" + } + }, + "node_modules/transformers/node_modules/source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==", + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/transformers/node_modules/uglify-js": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", + "integrity": "sha512-viLk+/8G0zm2aKt1JJAVcz5J/5ytdiNaIsKgrre3yvSUjwVG6ZUujGH7E2TiPigZUwLYCe7eaIUEP2Zka2VJPA==", + "dependencies": { + "optimist": "~0.3.5", + "source-map": "~0.1.7" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha512-qLq/4y2pjcU3vhlhseXGGJ7VbFO4pBANu0kwl8VCa9KEI0V8VfZIx2Fy3w01iSTA/pGwKZSmu/+I4etLNDdt5w==", + "license": "BSD-2-Clause", + "dependencies": { + "source-map": "~0.5.1", + "yargs": "~3.10.0" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + }, + "optionalDependencies": { + "uglify-to-browserify": "~1.0.0" + } + }, + "node_modules/uglify-js/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==", + "license": "MIT", + "optional": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/with": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/with/-/with-4.0.3.tgz", + "integrity": "sha512-mJZFpyEc1JTAdxhi/vhVeAM2S7vsltEKDiexDDo1HuAzlYKhcVUU6cwY8cHrFYdt82ZNkfKCeyhA3IYFegI0Kg==", + "license": "MIT", + "dependencies": { + "acorn": "^1.0.1", + "acorn-globals": "^1.0.3" + } + }, + "node_modules/with/node_modules/acorn": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz", + "integrity": "sha512-FsqWmApWGMGLKKNpHt12PMc5AK7BaZee0WRh04fCysmTzHe+rrKOa2MKjORhnzfpe4r0JnfdqHn02iDA9Dqj2A==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==", + "license": "MIT/X11", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A==", + "license": "MIT", + "dependencies": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..a7b967f --- /dev/null +++ b/backend/package.json @@ -0,0 +1,16 @@ +{ + "name": "backend", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "cookie-parser": "~1.4.4", + "debug": "~2.6.9", + "express": "~4.16.1", + "http-errors": "~1.6.3", + "jade": "~1.11.0", + "morgan": "~1.9.1" + } +} diff --git a/backend/public/stylesheets/style.css b/backend/public/stylesheets/style.css new file mode 100644 index 0000000..9453385 --- /dev/null +++ b/backend/public/stylesheets/style.css @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} diff --git a/backend/routes/index.js b/backend/routes/index.js new file mode 100644 index 0000000..ecca96a --- /dev/null +++ b/backend/routes/index.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET home page. */ +router.get('/', function(req, res, next) { + res.render('index', { title: 'Express' }); +}); + +module.exports = router; diff --git a/backend/routes/users.js b/backend/routes/users.js new file mode 100644 index 0000000..623e430 --- /dev/null +++ b/backend/routes/users.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET users listing. */ +router.get('/', function(req, res, next) { + res.send('respond with a resource'); +}); + +module.exports = router; diff --git a/backend/views/error.jade b/backend/views/error.jade new file mode 100644 index 0000000..51ec12c --- /dev/null +++ b/backend/views/error.jade @@ -0,0 +1,6 @@ +extends layout + +block content + h1= message + h2= error.status + pre #{error.stack} diff --git a/backend/views/index.jade b/backend/views/index.jade new file mode 100644 index 0000000..3d63b9a --- /dev/null +++ b/backend/views/index.jade @@ -0,0 +1,5 @@ +extends layout + +block content + h1= title + p Welcome to #{title} diff --git a/backend/views/layout.jade b/backend/views/layout.jade new file mode 100644 index 0000000..15af079 --- /dev/null +++ b/backend/views/layout.jade @@ -0,0 +1,7 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/stylesheets/style.css') + body + block content -- 2.49.1 From 2c671744c40371123ec3c42bc3e36a0b6bbb321a Mon Sep 17 00:00:00 2001 From: Will Baumbach Date: Wed, 22 Apr 2026 10:33:26 -0500 Subject: [PATCH 5/7] updating data models to have status for both week and day and updated calendar to complete based on status --- src/app/app.component.ts | 5 + src/app/calendar/calendar.component.html | 14 +- src/app/calendar/calendar.component.scss | 4 + src/app/calendar/calendar.component.ts | 32 +- src/app/models/plan.model.ts | 7 + src/app/models/workout.model.ts | 9 +- src/app/testData/examplePlan.ts | 398 +++++++++++++++-------- 7 files changed, 328 insertions(+), 141 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b7dbc73..13361f3 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -11,4 +11,9 @@ import { CalendarComponent } from "./calendar/calendar.component"; }) export class AppComponent { title = 'runningPlans'; + + ngOnInit(): void { + + + } } diff --git a/src/app/calendar/calendar.component.html b/src/app/calendar/calendar.component.html index 4f9c5f9..3347821 100644 --- a/src/app/calendar/calendar.component.html +++ b/src/app/calendar/calendar.component.html @@ -2,6 +2,7 @@ + @@ -9,21 +10,22 @@ - - @for (planWeek of examplePlan.planDetials; track $index) { + @for (planWeek of examplePlan.planDetials; track planWeek.week; let i = $index) { - - @for (workout of planWeek.workouts; track $index) { - + @for (workout of planWeek.workouts; track workout.day; let j = $index) { + } - + }
WeekTotal Milage Monday Tuesday WednesdayFriday Saturday SundayTotal Milage
+
Week {{planWeek.week}}
+ {{planWeek.totalMilage}}
{{workout.type}}
{{workout.totalDistance == 0 ? '' : workout.totalDistance + ' miles'}}
{{workout.description}}
{{planWeek.totalMilage}}
diff --git a/src/app/calendar/calendar.component.scss b/src/app/calendar/calendar.component.scss index fb1566a..f75abae 100644 --- a/src/app/calendar/calendar.component.scss +++ b/src/app/calendar/calendar.component.scss @@ -42,4 +42,8 @@ td { } .completedWorkout:hover { background-color: rgb(89, 161, 89); +} + +.completedWeek { + background-color: rgb(107, 194, 107); } \ No newline at end of file diff --git a/src/app/calendar/calendar.component.ts b/src/app/calendar/calendar.component.ts index 924e894..957c3ef 100644 --- a/src/app/calendar/calendar.component.ts +++ b/src/app/calendar/calendar.component.ts @@ -1,27 +1,45 @@ import { Component } from '@angular/core'; import { examplePlan } from '../testData/examplePlan'; import { Plan } from '../models/plan.model'; +import { CommonModule } from "@angular/common"; +import { Status } from '../models/workout.model'; @Component({ selector: 'calendar', standalone: true, - imports: [], + imports: [CommonModule], templateUrl: './calendar.component.html', styleUrl: './calendar.component.scss' }) export class CalendarComponent { examplePlan: Plan = examplePlan; + status = Status; ngOnInit(): void { } - onCellClick(event: Event) { - const target = event.currentTarget as HTMLElement; - if (target.classList.contains('incompleteWorkout')) { - target.classList.replace('incompleteWorkout', 'completedWorkout') - } else if (target.classList.contains('completedWorkout')) { - target.classList.replace('completedWorkout', 'incompleteWorkout') + onCellClick(weekNum: number, dayNum: number) { + let status = examplePlan.planDetials[weekNum].workouts[dayNum].status + if (status === Status.Incomplete) { + examplePlan.planDetials[weekNum].workouts[dayNum].status = Status.Complete + } else if (status === Status.Complete) { + examplePlan.planDetials[weekNum].workouts[dayNum].status = Status.Incomplete + } + this.checkCompletedWeek(weekNum) + } + + checkCompletedWeek(weekNum: number) { + let completeCount = 0; + examplePlan.planDetials[weekNum].workouts.forEach(workout => { + if (workout.status === Status.Complete) { + completeCount++ + } + }) + if (completeCount === 7) { + examplePlan.planDetials[weekNum].status = Status.Complete + } else { + examplePlan.planDetials[weekNum].status = Status.Incomplete } } } diff --git a/src/app/models/plan.model.ts b/src/app/models/plan.model.ts index 73743d8..17e07b5 100644 --- a/src/app/models/plan.model.ts +++ b/src/app/models/plan.model.ts @@ -12,5 +12,12 @@ export interface Plan { export interface Week { week: number, totalMilage: number, + status: Status, workouts: Workout[] +} + +export enum Status { + Complete = 'complete', + Incomplete = 'incomplete', + Skipped = 'skipped' } \ No newline at end of file diff --git a/src/app/models/workout.model.ts b/src/app/models/workout.model.ts index 91c1a2d..71419a4 100644 --- a/src/app/models/workout.model.ts +++ b/src/app/models/workout.model.ts @@ -2,7 +2,8 @@ export interface Workout { day: number, type: WorkoutType, totalDistance: number, - description: string + description: string, + status: Status } export enum WorkoutType { @@ -16,4 +17,10 @@ export enum WorkoutType { GeneralAerobic = "General Aerobic", Recovery = "Recovery", Rest = "Rest or Crosstrain" +} + +export enum Status { + Complete = 'complete', + Incomplete = 'incomplete', + Skipped = 'skipped' } \ No newline at end of file diff --git a/src/app/testData/examplePlan.ts b/src/app/testData/examplePlan.ts index 2428740..1e2ef60 100644 --- a/src/app/testData/examplePlan.ts +++ b/src/app/testData/examplePlan.ts @@ -1,5 +1,5 @@ import { Plan } from "../models/plan.model"; -import { Workout, WorkoutType } from "../models/workout.model"; +import { Status, Workout, WorkoutType } from "../models/workout.model"; export const examplePlan: Plan = { name: 'Pfitz 18/55', @@ -11,864 +11,1008 @@ export const examplePlan: Plan = { { week: 1, totalMilage: 31, + status: Status.Complete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Complete }, { day: 1, type: WorkoutType.Threshold, totalDistance: 8, - description: '20-25 min LT' + description: '20-25 min LT', + status: Status.Complete }, { day: 2, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Complete }, { day: 3, type: WorkoutType.GeneralAerobic, totalDistance: 8, - description: '' + description: '', + status: Status.Complete }, { day: 4, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Complete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Complete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 11, - description: '' + description: '', + status: Status.Complete } ] }, { week: 2, totalMilage: 33, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.GeneralAerobic, totalDistance: 8, - description: '6 x 10s Hills, 8 x 100m strides' + description: '6 x 10s Hills, 8 x 100m strides', + status: Status.Incomplete }, { day: 2, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 3, type: WorkoutType.GeneralAerobic, totalDistance: 9, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.MarathonPaceLongRun, totalDistance: 12, - description: '8 mi @ marathon pace' + description: '8 mi @ marathon pace', + status: Status.Incomplete } ] }, { week: 3, totalMilage: 39, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.GeneralAerobic, totalDistance: 9, - description: '' + description: '', + status: Status.Incomplete }, { day: 2, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Threshold, totalDistance: 8, - description: '25-30 min LT' + description: '25-30 min LT', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 14, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 4, totalMilage: 40, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.GeneralAerobic, totalDistance: 8, - description: '6 x 10s Hills, 8 x 100m strides' + description: '6 x 10s Hills, 8 x 100m strides', + status: Status.Incomplete }, { day: 2, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 3, type: WorkoutType.GeneralAerobic, totalDistance: 9, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 15, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 5, totalMilage: 42, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.Threshold, totalDistance: 9, - description: '30-35 min LT' + description: '30-35 min LT', + status: Status.Incomplete }, { day: 2, type: WorkoutType.Recovery, totalDistance: 5, - description: '' + description: '', + status: Status.Incomplete }, { day: 3, type: WorkoutType.GeneralAerobic, totalDistance: 9, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.MarathonPaceLongRun, totalDistance: 15, - description: '10 mi @ marathon pace' + description: '10 mi @ marathon pace', + status: Status.Incomplete } ] }, { week: 6, totalMilage: 33, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.GeneralAerobic, totalDistance: 7, - description: '10 x 100m strides' + description: '10 x 100m strides', + status: Status.Incomplete }, { day: 2, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 3, type: WorkoutType.GeneralAerobic, totalDistance: 7, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 11, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 7, totalMilage: 48, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.Threshold, totalDistance: 9, - description: '30-35 min LT' + description: '30-35 min LT', + status: Status.Incomplete }, { day: 2, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 3, type: WorkoutType.MediumLongRun, totalDistance: 11, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.GeneralAerobic, totalDistance: 7, - description: '6 x 10s Hills, 8 x 100m strides' + description: '6 x 10s Hills, 8 x 100m strides', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 17, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 8, totalMilage: 51, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.Recovery, totalDistance: 6, - description: '6x100 strides' + description: '6x100 strides', + status: Status.Incomplete }, { day: 2, type: WorkoutType.MediumLongRun, totalDistance: 12, - description: '' + description: '', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Threshold, totalDistance: 10, - description: '35-40 min LT' + description: '35-40 min LT', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 19, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 9, totalMilage: 46, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.Recovery, totalDistance: 6, - description: '' + description: '', + status: Status.Incomplete }, { day: 2, type: WorkoutType.MediumLongRun, totalDistance: 13, - description: '' + description: '', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Recovery, totalDistance: 6, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 5, - description: '6x100 strides' + description: '6x100 strides', + status: Status.Incomplete }, { day: 6, type: WorkoutType.MarathonPaceLongRun, totalDistance: 16, - description: '12 mi @ marathon pace' + description: '12 mi @ marathon pace', + status: Status.Incomplete } ] }, { week: 10, totalMilage: 40, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.GeneralAerobic, totalDistance: 8, - description: '' + description: '', + status: Status.Incomplete }, { day: 2, type: WorkoutType.VO2Max, totalDistance: 8, - description: '6 x 600m @ 5K pace' + description: '6 x 600m @ 5K pace', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.GeneralAerobic, totalDistance: 7, - description: '6 x 10s Hills, 8 x 100m strides' + description: '6 x 10s Hills, 8 x 100m strides', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 13, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 11, totalMilage: 52, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.Recovery, totalDistance: 6, - description: '6x100 strides' + description: '6x100 strides', + status: Status.Incomplete }, { day: 2, type: WorkoutType.Threshold, totalDistance: 11, - description: '35-45 min LT' + description: '35-45 min LT', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.MediumLongRun, totalDistance: 12, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 19, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 12, totalMilage: 48, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.GeneralAerobic, totalDistance: 10, - description: '' + description: '', + status: Status.Incomplete }, { day: 2, type: WorkoutType.VO2Max, totalDistance: 8, - description: '5 x 600m @ 5K pace' + description: '5 x 600m @ 5K pace', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Recovery, totalDistance: 4, - description: '6x100 strides' + description: '6x100 strides', + status: Status.Incomplete }, { day: 5, type: WorkoutType.VO2Max, totalDistance: 10, - description: '8K-15K Tune Up' + description: '8K-15K Tune Up', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 16, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 13, totalMilage: 48, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.Recovery, totalDistance: 6, - description: '' + description: '', + status: Status.Incomplete }, { day: 2, type: WorkoutType.VO2Max, totalDistance: 9, - description: '5 x 1000m @ 5K pace' + description: '5 x 1000m @ 5K pace', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.MediumLongRun, totalDistance: 11, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 5, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.MarathonPaceLongRun, totalDistance: 17, - description: '14 mi @ marathon pace' + description: '14 mi @ marathon pace', + status: Status.Incomplete } ] }, { week: 14, totalMilage: 48, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.GeneralAerobic, totalDistance: 10, - description: '' + description: '', + status: Status.Incomplete }, { day: 2, type: WorkoutType.VO2Max, totalDistance: 8, - description: '5 x 600m @ 5K pace' + description: '5 x 600m @ 5K pace', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Recovery, totalDistance: 4, - description: '6x100 strides' + description: '6x100 strides', + status: Status.Incomplete }, { day: 5, type: WorkoutType.VO2Max, totalDistance: 10, - description: '8K-15K Tune Up' + description: '8K-15K Tune Up', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 16, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 15, totalMilage: 49, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.Recovery, totalDistance: 6, - description: '' + description: '', + status: Status.Incomplete }, { day: 2, type: WorkoutType.VO2Max, totalDistance: 9, - description: '5 x 1000m @ 5K pace' + description: '5 x 1000m @ 5K pace', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.MediumLongRun, totalDistance: 11, - description: '' + description: '', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 4, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 19, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 16, totalMilage: 41, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.GeneralAerobic, totalDistance: 7, - description: '6 x 10s Hills, 10 x 100m strides' + description: '6 x 10s Hills, 10 x 100m strides', + status: Status.Incomplete }, { day: 2, type: WorkoutType.Recovery, totalDistance: 5, - description: '' + description: '', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Recovery, totalDistance: 4, - description: '4-5 mi w/ 6x100 strides' + description: '4-5 mi w/ 6x100 strides', + status: Status.Incomplete }, { day: 5, type: WorkoutType.VO2Max, totalDistance: 9, - description: '8K-10K Tune Up' + description: '8K-10K Tune Up', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 16, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 17, totalMilage: 31, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.Recovery, totalDistance: 6, - description: '8x100 strides' + description: '8x100 strides', + status: Status.Incomplete }, { day: 2, type: WorkoutType.VO2Max, totalDistance: 8, - description: '5 x 1000m @ 5K pace' + description: '5 x 1000m @ 5K pace', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Recovery, totalDistance: 5, - description: '6x100 strides' + description: '6x100 strides', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.LongRun, totalDistance: 12, - description: '' + description: '', + status: Status.Incomplete } ] }, { week: 18, totalMilage: 16, + status: Status.Incomplete, workouts: [ { day: 0, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 1, type: WorkoutType.Recovery, totalDistance: 5, - description: '8x100 strides' + description: '8x100 strides', + status: Status.Incomplete }, { day: 2, type: WorkoutType.DressRehersal, totalDistance: 5, - description: '2 mi @ marathon pace' + description: '2 mi @ marathon pace', + status: Status.Incomplete }, { day: 3, type: WorkoutType.Rest, totalDistance: 0, - description: '' + description: '', + status: Status.Incomplete }, { day: 4, type: WorkoutType.Recovery, totalDistance: 4, - description: '6x100 strides' + description: '6x100 strides', + status: Status.Incomplete }, { day: 5, type: WorkoutType.Recovery, totalDistance: 2, - description: '' + description: '', + status: Status.Incomplete }, { day: 6, type: WorkoutType.RaceDay, totalDistance: 26.2, - description: '' + description: '', + status: Status.Incomplete } ] } -- 2.49.1 From 99008d98f52a53b03624e53cc9dc53196f908148 Mon Sep 17 00:00:00 2001 From: Will Baumbach Date: Wed, 22 Apr 2026 10:47:07 -0500 Subject: [PATCH 6/7] adding .env to gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 04bc98e..1dd1de0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ # Node /node_modules -/backend/node_modules npm-debug.log yarn-error.log @@ -41,3 +40,7 @@ testem.log # System files .DS_Store Thumbs.db + +# Backend +/backend/node_modules +/bakend/.env \ No newline at end of file -- 2.49.1 From 5c5b30e84efe4951beb9269cfdc9af163e07cee7 Mon Sep 17 00:00:00 2001 From: Will Baumbach Date: Wed, 22 Apr 2026 12:51:01 -0500 Subject: [PATCH 7/7] adding angular material and ability to hide completed weeks --- angular.json | 2 + package-lock.json | 896 ++++++++++++++++++++++- package.json | 4 +- src/app/app.config.ts | 3 +- src/app/calendar/calendar.component.html | 11 +- src/app/calendar/calendar.component.ts | 8 +- src/index.html | 4 +- src/styles.scss | 3 + 8 files changed, 922 insertions(+), 9 deletions(-) diff --git a/angular.json b/angular.json index 86ff944..1bf067c 100644 --- a/angular.json +++ b/angular.json @@ -30,6 +30,7 @@ "src/assets" ], "styles": [ + "@angular/material/prebuilt-themes/deeppurple-amber.css", "src/styles.scss" ], "scripts": [] @@ -90,6 +91,7 @@ "src/assets" ], "styles": [ + "@angular/material/prebuilt-themes/deeppurple-amber.css", "src/styles.scss" ], "scripts": [] diff --git a/package-lock.json b/package-lock.json index 814cb81..8db97f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,12 @@ "version": "0.0.0", "dependencies": { "@angular/animations": "^17.3.0", + "@angular/cdk": "^17.3.10", "@angular/common": "^17.3.0", "@angular/compiler": "^17.3.0", "@angular/core": "^17.3.0", "@angular/forms": "^17.3.0", + "@angular/material": "^17.3.10", "@angular/platform-browser": "^17.3.0", "@angular/platform-browser-dynamic": "^17.3.0", "@angular/router": "^17.3.0", @@ -332,6 +334,23 @@ "@angular/core": "17.3.12" } }, + "node_modules/@angular/cdk": { + "version": "17.3.10", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.3.10.tgz", + "integrity": "sha512-b1qktT2c1TTTe5nTji/kFAVW92fULK0YhYAvJ+BjZTPKu2FniZNe8o4qqQ0pUuvtMu+ZQxp/QqFYoidIVCjScg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/cli": { "version": "17.3.17", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.3.17.tgz", @@ -514,6 +533,71 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/material": { + "version": "17.3.10", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-17.3.10.tgz", + "integrity": "sha512-hHMQES0tQPH5JW33W+mpBPuM8ybsloDTqFPuRV8cboDjosAWfJhzAKF3ozICpNlUrs62La/2Wu/756GcQrxebg==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/auto-init": "15.0.0-canary.7f224ddd4.0", + "@material/banner": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/button": "15.0.0-canary.7f224ddd4.0", + "@material/card": "15.0.0-canary.7f224ddd4.0", + "@material/checkbox": "15.0.0-canary.7f224ddd4.0", + "@material/chips": "15.0.0-canary.7f224ddd4.0", + "@material/circular-progress": "15.0.0-canary.7f224ddd4.0", + "@material/data-table": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dialog": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/drawer": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/fab": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/floating-label": "15.0.0-canary.7f224ddd4.0", + "@material/form-field": "15.0.0-canary.7f224ddd4.0", + "@material/icon-button": "15.0.0-canary.7f224ddd4.0", + "@material/image-list": "15.0.0-canary.7f224ddd4.0", + "@material/layout-grid": "15.0.0-canary.7f224ddd4.0", + "@material/line-ripple": "15.0.0-canary.7f224ddd4.0", + "@material/linear-progress": "15.0.0-canary.7f224ddd4.0", + "@material/list": "15.0.0-canary.7f224ddd4.0", + "@material/menu": "15.0.0-canary.7f224ddd4.0", + "@material/menu-surface": "15.0.0-canary.7f224ddd4.0", + "@material/notched-outline": "15.0.0-canary.7f224ddd4.0", + "@material/radio": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/segmented-button": "15.0.0-canary.7f224ddd4.0", + "@material/select": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/slider": "15.0.0-canary.7f224ddd4.0", + "@material/snackbar": "15.0.0-canary.7f224ddd4.0", + "@material/switch": "15.0.0-canary.7f224ddd4.0", + "@material/tab": "15.0.0-canary.7f224ddd4.0", + "@material/tab-bar": "15.0.0-canary.7f224ddd4.0", + "@material/tab-indicator": "15.0.0-canary.7f224ddd4.0", + "@material/tab-scroller": "15.0.0-canary.7f224ddd4.0", + "@material/textfield": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tooltip": "15.0.0-canary.7f224ddd4.0", + "@material/top-app-bar": "15.0.0-canary.7f224ddd4.0", + "@material/touch-target": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^17.0.0 || ^18.0.0", + "@angular/cdk": "17.3.10", + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", + "@angular/forms": "^17.0.0 || ^18.0.0", + "@angular/platform-browser": "^17.0.0 || ^18.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/platform-browser": { "version": "17.3.12", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.12.tgz", @@ -2896,6 +2980,808 @@ "node": ">= 0.4" } }, + "node_modules/@material/animation": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-1GSJaPKef+7HRuV+HusVZHps64cmZuOItDbt40tjJVaikcaZvwmHlcTxRIqzcRoCdt5ZKHh3NoO7GB9Khg4Jnw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/auto-init": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/auto-init/-/auto-init-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-t7ZGpRJ3ec0QDUO0nJu/SMgLW7qcuG2KqIsEYD1Ej8qhI2xpdR2ydSDQOkVEitXmKoGol1oq4nYSBjTlB65GqA==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/banner": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/banner/-/banner-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-g9wBUZzYBizyBcBQXTIafnRUUPi7efU9gPJfzeGgkynXiccP/vh5XMmH+PBxl5v+4MlP/d4cZ2NUYoAN7UTqSA==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/button": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/base": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/base/-/base-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-I9KQOKXpLfJkP8MqZyr8wZIzdPHrwPjFvGd9zSK91/vPyE4hzHRJc/0njsh9g8Lm9PRYLbifXX+719uTbHxx+A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/button": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/button/-/button-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-BHB7iyHgRVH+JF16+iscR+Qaic+p7LU1FOLgP8KucRlpF9tTwIxQA6mJwGRi5gUtcG+vyCmzVS+hIQ6DqT/7BA==", + "license": "MIT", + "dependencies": { + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/touch-target": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/card": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/card/-/card-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-kt7y9/IWOtJTr3Z/AoWJT3ZLN7CLlzXhx2udCLP9ootZU2bfGK0lzNwmo80bv/pJfrY9ihQKCtuGTtNxUy+vIw==", + "license": "MIT", + "dependencies": { + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/checkbox": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-rURcrL5O1u6hzWR+dNgiQ/n89vk6tdmdP3mZgnxJx61q4I/k1yijKqNJSLrkXH7Rto3bM5NRKMOlgvMvVd7UMQ==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/touch-target": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/chips": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/chips/-/chips-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-AYAivV3GSk/T/nRIpH27sOHFPaSMrE3L0WYbnb5Wa93FgY8a0fbsFYtSH2QmtwnzXveg+B1zGTt7/xIIcynKdQ==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/checkbox": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/touch-target": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/circular-progress": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-DJrqCKb+LuGtjNvKl8XigvyK02y36GRkfhMUYTcJEi3PrOE00bwXtyj7ilhzEVshQiXg6AHGWXtf5UqwNrx3Ow==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/progress-indicator": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/data-table": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/data-table/-/data-table-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-/2WZsuBIq9z9RWYF5Jo6b7P6u0fwit+29/mN7rmAZ6akqUR54nXyNfoSNiyydMkzPlZZsep5KrSHododDhBZbA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/checkbox": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/icon-button": "15.0.0-canary.7f224ddd4.0", + "@material/linear-progress": "15.0.0-canary.7f224ddd4.0", + "@material/list": "15.0.0-canary.7f224ddd4.0", + "@material/menu": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/select": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/touch-target": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/density": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/density/-/density-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-o9EXmGKVpiQ6mHhyV3oDDzc78Ow3E7v8dlaOhgaDSXgmqaE8v5sIlLNa/LKSyUga83/fpGk3QViSGXotpQx0jA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/dialog": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-u0XpTlv1JqWC/bQ3DavJ1JguofTelLT2wloj59l3/1b60jv42JQ6Am7jU3I8/SIUB1MKaW7dYocXjDWtWJakLA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/button": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/icon-button": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/touch-target": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/dom": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/dom/-/dom-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-mQ1HT186GPQSkRg5S18i70typ5ZytfjL09R0gJ2Qg5/G+MLCGi7TAjZZSH65tuD/QGOjel4rDdWOTmYbPYV6HA==", + "license": "MIT", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/drawer": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/drawer/-/drawer-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-qyO0W0KBftfH8dlLR0gVAgv7ZHNvU8ae11Ao6zJif/YxcvK4+gph1z8AO4H410YmC2kZiwpSKyxM1iQCCzbb4g==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/list": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/elevation": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-tV6s4/pUBECedaI36Yj18KmRCk1vfue/JP/5yYRlFNnLMRVISePbZaKkn/BHXVf+26I3W879+XqIGlDVdmOoMA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/fab": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/fab/-/fab-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-4h76QrzfZTcPdd+awDPZ4Q0YdSqsXQnS540TPtyXUJ/5G99V6VwGpjMPIxAsW0y+pmI9UkLL/srrMaJec+7r4Q==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/touch-target": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/feature-targeting": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-SAjtxYh6YlKZriU83diDEQ7jNSP2MnxKsER0TvFeyG1vX/DWsUyYDOIJTOEa9K1N+fgJEBkNK8hY55QhQaspew==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/floating-label": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-0KMo5ijjYaEHPiZ2pCVIcbaTS2LycvH9zEhEMKwPPGssBCX7iz5ffYQFk7e5yrQand1r3jnQQgYfHAwtykArnQ==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/focus-ring": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/focus-ring/-/focus-ring-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-Jmg1nltq4J6S6A10EGMZnvufrvU3YTi+8R8ZD9lkSbun0Fm2TVdICQt/Auyi6An9zP66oQN6c31eqO6KfIPsDg==", + "license": "MIT", + "dependencies": { + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0" + } + }, + "node_modules/@material/form-field": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-fEPWgDQEPJ6WF7hNnIStxucHR9LE4DoDSMqCsGWS2Yu+NLZYLuCEecgR0UqQsl1EQdNRaFh8VH93KuxGd2hiPg==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/icon-button": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-DcK7IL4ICY/DW+48YQZZs9g0U1kRaW0Wb0BxhvppDMYziHo/CTpFdle4gjyuTyRxPOdHQz5a97ru48Z9O4muTw==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/touch-target": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/image-list": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/image-list/-/image-list-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-voMjG2p80XbjL1B2lmF65zO5gEgJOVKClLdqh4wbYzYfwY/SR9c8eLvlYG7DLdFaFBl/7gGxD8TvvZ329HUFPw==", + "license": "MIT", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/layout-grid": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-veDABLxMn2RmvfnUO2RUmC1OFfWr4cU+MrxKPoDD2hl3l3eDYv5fxws6r5T1JoSyXoaN+oEZpheS0+M9Ure8Pg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/line-ripple": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-f60hVJhIU6I3/17Tqqzch1emUKEcfVVgHVqADbU14JD+oEIz429ZX9ksZ3VChoU3+eejFl+jVdZMLE/LrAuwpg==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/linear-progress": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-pRDEwPQielDiC9Sc5XhCXrGxP8wWOnAO8sQlMebfBYHYqy5hhiIzibezS8CSaW4MFQFyXmCmpmqWlbqGYRmiyg==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/progress-indicator": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/list": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/list/-/list-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-Is0NV91sJlXF5pOebYAtWLF4wU2MJDbYqztML/zQNENkQxDOvEXu3nWNb3YScMIYJJXvARO0Liur5K4yPagS1Q==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/menu": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/menu/-/menu-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-D11QU1dXqLbh5X1zKlEhS3QWh0b5BPNXlafc5MXfkdJHhOiieb7LC9hMJhbrHtj24FadJ7evaFW/T2ugJbJNnQ==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/list": "15.0.0-canary.7f224ddd4.0", + "@material/menu-surface": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/menu-surface": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/menu-surface/-/menu-surface-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-7RZHvw0gbwppaAJ/Oh5SWmfAKJ62aw1IMB3+3MRwsb5PLoV666wInYa+zJfE4i7qBeOn904xqT2Nko5hY0ssrg==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/notched-outline": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-Yg2usuKB2DKlKIBISbie9BFsOVuffF71xjbxPbybvqemxqUBd+bD5/t6H1fLE+F8/NCu5JMigho4ewUU+0RCiw==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/floating-label": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/progress-indicator": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-UPbDjE5CqT+SqTs0mNFG6uFEw7wBlgYmh+noSkQ6ty/EURm8lF125dmi4dv4kW0+octonMXqkGtAoZwLIHKf/w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/radio": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/radio/-/radio-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-wR1X0Sr0KmQLu6+YOFKAI84G3L6psqd7Kys5kfb8WKBM36zxO5HQXC5nJm/Y0rdn22ixzsIz2GBo0MNU4V4k1A==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/touch-target": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/ripple": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-JqOsWM1f4aGdotP0rh1vZlPZTg6lZgh39FIYHFMfOwfhR+LAikUJ+37ciqZuewgzXB6iiRO6a8aUH6HR5SJYPg==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/rtl": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-UVf14qAtmPiaaZjuJtmN36HETyoKWmsZM/qn1L5ciR2URb8O035dFWnz4ZWFMmAYBno/L7JiZaCkPurv2ZNrGA==", + "license": "MIT", + "dependencies": { + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/segmented-button": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/segmented-button/-/segmented-button-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-LCnVRUSAhELTKI/9hSvyvIvQIpPpqF29BV+O9yM4WoNNmNWqTulvuiv7grHZl6Z+kJuxSg4BGbsPxxb9dXozPg==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/touch-target": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/select": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/select/-/select-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-WioZtQEXRpglum0cMSzSqocnhsGRr+ZIhvKb3FlaNrTaK8H3Y4QA7rVjv3emRtrLOOjaT6/RiIaUMTo9AGzWQQ==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/floating-label": "15.0.0-canary.7f224ddd4.0", + "@material/line-ripple": "15.0.0-canary.7f224ddd4.0", + "@material/list": "15.0.0-canary.7f224ddd4.0", + "@material/menu": "15.0.0-canary.7f224ddd4.0", + "@material/menu-surface": "15.0.0-canary.7f224ddd4.0", + "@material/notched-outline": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/shape": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/shape/-/shape-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-8z8l1W3+cymObunJoRhwFPKZ+FyECfJ4MJykNiaZq7XJFZkV6xNmqAVrrbQj93FtLsECn9g4PjjIomguVn/OEw==", + "license": "MIT", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/slider": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/slider/-/slider-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-QU/WSaSWlLKQRqOhJrPgm29wqvvzRusMqwAcrCh1JTrCl+xwJ43q5WLDfjYhubeKtrEEgGu9tekkAiYfMG7EBw==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/snackbar": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/snackbar/-/snackbar-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-sm7EbVKddaXpT/aXAYBdPoN0k8yeg9+dprgBUkrdqGzWJAeCkxb4fv2B3He88YiCtvkTz2KLY4CThPQBSEsMFQ==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/button": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/icon-button": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/switch": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/switch/-/switch-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-lEDJfRvkVyyeHWIBfoxYjJVl+WlEAE2kZ/+6OqB1FW0OV8ftTODZGhHRSzjVBA1/p4FPuhAtKtoK9jTpa4AZjA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/tab/-/tab-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-E1xGACImyCLurhnizyOTCgOiVezce4HlBFAI6YhJo/AyVwjN2Dtas4ZLQMvvWWqpyhITNkeYdOchwCC1mrz3AQ==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/tab-indicator": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab-bar": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-p1Asb2NzrcECvAQU3b2SYrpyJGyJLQWR+nXTYzDKE8WOpLIRCXap2audNqD7fvN/A20UJ1J8U01ptrvCkwJ4eA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/tab": "15.0.0-canary.7f224ddd4.0", + "@material/tab-indicator": "15.0.0-canary.7f224ddd4.0", + "@material/tab-scroller": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab-indicator": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-h9Td3MPqbs33spcPS7ecByRHraYgU4tNCZpZzZXw31RypjKvISDv/PS5wcA4RmWqNGih78T7xg4QIGsZg4Pk4w==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab-scroller": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-LFeYNjQpdXecwECd8UaqHYbhscDCwhGln5Yh+3ctvcEgvmDPNjhKn/DL3sWprWvG8NAhP6sHMrsGhQFVdCWtTg==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/tab": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/textfield": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-AExmFvgE5nNF0UA4l2cSzPghtxSUQeeoyRjFLHLy+oAaE4eKZFrSy0zEpqPeWPQpEMDZk+6Y+6T3cOFYBeSvsw==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/density": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/floating-label": "15.0.0-canary.7f224ddd4.0", + "@material/line-ripple": "15.0.0-canary.7f224ddd4.0", + "@material/notched-outline": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/theme": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-hs45hJoE9yVnoVOcsN1jklyOa51U4lzWsEnQEuJTPOk2+0HqCQ0yv/q0InpSnm2i69fNSyZC60+8HADZGF8ugQ==", + "license": "MIT", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tokens": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-r9TDoicmcT7FhUXC4eYMFnt9TZsz0G8T3wXvkKncLppYvZ517gPyD/1+yhuGfGOxAzxTrM66S/oEc1fFE2q4hw==", + "license": "MIT", + "dependencies": { + "@material/elevation": "15.0.0-canary.7f224ddd4.0" + } + }, + "node_modules/@material/tooltip": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/tooltip/-/tooltip-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-8qNk3pmPLTnam3XYC1sZuplQXW9xLn4Z4MI3D+U17Q7pfNZfoOugGr+d2cLA9yWAEjVJYB0mj8Yu86+udo4N9w==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/button": "15.0.0-canary.7f224ddd4.0", + "@material/dom": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/top-app-bar": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/top-app-bar/-/top-app-bar-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-SARR5/ClYT4CLe9qAXakbr0i0cMY0V3V4pe3ElIJPfL2Z2c4wGR1mTR8m2LxU1MfGKK8aRoUdtfKaxWejp+eNA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.7f224ddd4.0", + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/elevation": "15.0.0-canary.7f224ddd4.0", + "@material/ripple": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/shape": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/typography": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/touch-target": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-BJo/wFKHPYLGsRaIpd7vsQwKr02LtO2e89Psv0on/p0OephlNIgeB9dD9W+bQmaeZsZ6liKSKRl6wJWDiK71PA==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.7f224ddd4.0", + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/rtl": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/typography": { + "version": "15.0.0-canary.7f224ddd4.0", + "resolved": "https://registry.npmjs.org/@material/typography/-/typography-15.0.0-canary.7f224ddd4.0.tgz", + "integrity": "sha512-kBaZeCGD50iq1DeRRH5OM5Jl7Gdk+/NOfKArkY4ksBZvJiStJ7ACAhpvb8MEGm4s3jvDInQFLsDq3hL+SA79sQ==", + "license": "MIT", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", + "@material/theme": "15.0.0-canary.7f224ddd4.0", + "tslib": "^2.1.0" + } + }, "node_modules/@ngtools/webpack": { "version": "17.3.17", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.3.17.tgz", @@ -9681,7 +10567,7 @@ "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "entities": "^6.0.0" @@ -9722,7 +10608,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -10752,6 +11638,12 @@ "dev": true, "license": "MIT" }, + "node_modules/safevalues": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/safevalues/-/safevalues-0.3.4.tgz", + "integrity": "sha512-LRneZZRXNgjzwG4bDQdOTSbze3fHm1EAKN/8bePxnlEZiBmkYEDggaHbuvHI9/hoqHbGfsEA7tWS9GhYHZBBsw==", + "license": "Apache-2.0" + }, "node_modules/sass": { "version": "1.71.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", diff --git a/package.json b/package.json index 8fb3c90..35b3ed8 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,12 @@ "private": true, "dependencies": { "@angular/animations": "^17.3.0", + "@angular/cdk": "^17.3.10", "@angular/common": "^17.3.0", "@angular/compiler": "^17.3.0", "@angular/core": "^17.3.0", "@angular/forms": "^17.3.0", + "@angular/material": "^17.3.10", "@angular/platform-browser": "^17.3.0", "@angular/platform-browser-dynamic": "^17.3.0", "@angular/router": "^17.3.0", @@ -35,4 +37,4 @@ "karma-jasmine-html-reporter": "~2.1.0", "typescript": "~5.4.2" } -} +} \ No newline at end of file diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 6c6ef60..d3358e9 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -2,7 +2,8 @@ import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; export const appConfig: ApplicationConfig = { - providers: [provideRouter(routes)] + providers: [provideRouter(routes), provideAnimationsAsync()] }; diff --git a/src/app/calendar/calendar.component.html b/src/app/calendar/calendar.component.html index 3347821..db49583 100644 --- a/src/app/calendar/calendar.component.html +++ b/src/app/calendar/calendar.component.html @@ -1,8 +1,12 @@
+
+ Hide completed + weeks +
+ - @@ -12,11 +16,12 @@ @for (planWeek of examplePlan.planDetials; track planWeek.week; let i = $index) { + @if (!hideCompletedWeeks || (hideCompletedWeeks && planWeek.status != status.Complete)) { - @for (workout of planWeek.workouts; track workout.day; let j = $index) { } - } + }
WeekTotal Milage Monday Tuesday WednesdaySunday
Week {{planWeek.week}}
+
Total Milage: {{planWeek.totalMilage}}
{{planWeek.totalMilage}} @@ -25,8 +30,8 @@
{{workout.description}}
\ No newline at end of file diff --git a/src/app/calendar/calendar.component.ts b/src/app/calendar/calendar.component.ts index 957c3ef..9e919d6 100644 --- a/src/app/calendar/calendar.component.ts +++ b/src/app/calendar/calendar.component.ts @@ -3,17 +3,19 @@ import { examplePlan } from '../testData/examplePlan'; import { Plan } from '../models/plan.model'; import { CommonModule } from "@angular/common"; import { Status } from '../models/workout.model'; +import {MatCheckboxModule} from '@angular/material/checkbox'; @Component({ selector: 'calendar', standalone: true, - imports: [CommonModule], + imports: [CommonModule, MatCheckboxModule], templateUrl: './calendar.component.html', styleUrl: './calendar.component.scss' }) export class CalendarComponent { examplePlan: Plan = examplePlan; status = Status; + hideCompletedWeeks = true ngOnInit(): void { @@ -42,4 +44,8 @@ export class CalendarComponent { examplePlan.planDetials[weekNum].status = Status.Incomplete } } + + toggleHideCompletedWeeks() { + this.hideCompletedWeeks = !this.hideCompletedWeeks + } } diff --git a/src/index.html b/src/index.html index efce897..450b00e 100644 --- a/src/index.html +++ b/src/index.html @@ -6,8 +6,10 @@ + + - + diff --git a/src/styles.scss b/src/styles.scss index 90d4ee0..7e7239a 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1 +1,4 @@ /* You can add global styles to this file, and also import other style files */ + +html, body { height: 100%; } +body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } -- 2.49.1