Stubbing out meal plan page, created meal component

This commit is contained in:
Z. Charles Dziura 2025-06-09 11:49:14 -04:00
parent 0ff95a4e16
commit 63368aec0b
20 changed files with 9235 additions and 8975 deletions

147
.gitignore vendored
View file

@ -219,4 +219,149 @@ e2e/*.map
# System Files
.DS_Store/
# End of https://www.toptal.com/developers/gitignore/api/angular
# End of https://www.toptal.com/developers/gitignore/api/angular
# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
# End of https://www.toptal.com/developers/gitignore/api/node

3
webapp/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}

17838
webapp/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,35 +1,36 @@
{
"name": "webapp",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/common": "^20.0.0",
"@angular/compiler": "^20.0.0",
"@angular/core": "^20.0.0",
"@angular/forms": "^20.0.0",
"@angular/platform-browser": "^20.0.0",
"@angular/router": "^20.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0"
},
"devDependencies": {
"@angular/build": "^20.0.1",
"@angular/cli": "^20.0.1",
"@angular/compiler-cli": "^20.0.0",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.7.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.8.2"
}
"name": "webapp",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/common": "^20.0.0",
"@angular/compiler": "^20.0.0",
"@angular/core": "^20.0.0",
"@angular/forms": "^20.0.0",
"@angular/platform-browser": "^20.0.0",
"@angular/router": "^20.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"uuid": "^11.1.0"
},
"devDependencies": {
"@angular/build": "^20.0.1",
"@angular/cli": "^20.0.1",
"@angular/compiler-cli": "^20.0.0",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.8.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.8.2"
}
}

View file

@ -1,8 +1,14 @@
import { Routes } from '@angular/router';
import { Plan } from './routes/plan/plan';
export const routes: Routes = [
{
path: '',
loadComponent: () => import('./routes/home/home').then(c => c.Home)
}
pathMatch: 'full',
redirectTo: '/plan'
},
{
path: 'plan',
component: Plan
},
];

View file

@ -8,6 +8,4 @@ import { NavRail } from './components/nav-rail/nav-rail';
styleUrl: './app.scss',
templateUrl: './app.html',
})
export class App {
protected title = 'webapp';
}
export class App {}

View file

@ -9,7 +9,7 @@ nav {
display: flex;
flex-direction: column;
list-style: none;
padding: 1.2em;
padding: 1.8em 1.2em;
.nav-list__item {
align-items: center;
@ -18,7 +18,7 @@ nav {
inline-size: min-content;
&:not(:last-of-type) {
margin-bottom: 1.2em;
margin-bottom: 1.8em;
}
.nav-list__item-label {

View file

@ -0,0 +1,15 @@
export class TMeal {
constructor(readonly id: string, readonly type: MealType, readonly foods: Array<IFood>, readonly name?: string) {}
get totalCalories(): number {
return this.foods.map(food => food.calories).reduce((a, b) => a + b);
}
}
export type MealType = 'breakfast' | 'lunch' | 'dinner' | 'snack';
export interface IFood {
name: string;
servings: number;
calories: number;
}

View file

@ -1 +0,0 @@
<p>home works!</p>

View file

@ -1,14 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-home',
imports: [],
templateUrl: './home.html',
styleUrl: './home.scss',
})
export class Home implements OnInit {
constructor() {}
ngOnInit(): void {
}
}

View file

@ -0,0 +1,3 @@
<header>
{{ name }}
</header>

View file

@ -0,0 +1,8 @@
:host {
display: grid;
grid-template-areas:
"head"
"body"
"foot";
grid-template-rows: min-content auto min-content;
}

View file

@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Home } from './home';
import { Meal } from './meal';
describe('Home', () => {
let component: Home;
let fixture: ComponentFixture<Home>;
describe('Meal', () => {
let component: Meal;
let fixture: ComponentFixture<Meal>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [Home]
imports: [Meal]
})
.compileComponents();
fixture = TestBed.createComponent(Home);
fixture = TestBed.createComponent(Meal);
component = fixture.componentInstance;
fixture.detectChanges();
});

View file

@ -0,0 +1,19 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { MealType } from 'models/meal.model';
@Component({
selector: 'app-meal',
imports: [],
templateUrl: './meal.html',
styleUrl: './meal.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Meal {
@Input() type: MealType | undefined;
@Input('name') _name?: string;
get name(): string {
const name = this._name ?? (this.type as string);
return name.charAt(0).toLocaleUpperCase() + name.slice(1);
}
}

View file

@ -0,0 +1,6 @@
<header>Header!</header>
<section class="meal-plan">
@for (meal of meals(); track meal.id) {
<app-meal [type]="meal.type" [name]="meal.name"></app-meal>
}
</section>

View file

@ -0,0 +1,16 @@
:host {
display: grid;
grid-template-areas:
"head"
"body";
grid-template-rows: min-content auto;
height: 100%;
}
header {
grid-area: 'head';
}
.meal-plan {
grid-area: "body";
}

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Plan } from './plan';
describe('Plan', () => {
let component: Plan;
let fixture: ComponentFixture<Plan>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [Plan]
})
.compileComponents();
fixture = TestBed.createComponent(Plan);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,15 @@
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { v7 as uuidv7 } from 'uuid';
import { TMeal } from '../../models/meal.model';
import { Meal } from './components/meal/meal';
@Component({
selector: 'app-plan',
imports: [Meal],
templateUrl: './plan.html',
styleUrl: './plan.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Plan {
protected meals = signal<Array<TMeal>>([new TMeal(uuidv7(), 'breakfast', [])]);
}

View file

@ -13,7 +13,10 @@
"experimentalDecorators": true,
"importHelpers": true,
"target": "ES2022",
"module": "preserve"
"module": "preserve",
"paths": {
"models/*": ["./src/app/models/*"]
},
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,