+

{{title}}

- -
- - - Rocket Ship - - - - - - - - - - {{ title }} app is running! - - - Rocket Ship Smoke - - - +
+ + +
- -

Resources

-

Here are some links to help you get started:

- - - - -

Next Steps

-

What do you want to do next with your app?

- - - -
- - - - - - - - - - - -
- - -
-
ng generate component xyz
-
ng add @angular/material
-
ng add @angular/pwa
-
ng add _____
-
ng test
-
ng build
-
- - - - - - - - - Gray Clouds Background - - - +
- - - - - - - - - diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts deleted file mode 100644 index 7312d55..0000000 --- a/src/app/app.component.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(() => TestBed.configureTestingModule({ - imports: [RouterTestingModule], - declarations: [AppComponent] - })); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it(`should have as title 'module-configurator'`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('module-configurator'); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('.content span')?.textContent).toContain('module-configurator app is running!'); - }); -}); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e688f29..2096509 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -5,6 +5,7 @@ import { Component } from '@angular/core'; templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) + export class AppComponent { - title = 'module-configurator'; + title = 'Modul Konfigurator'; } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b1c6c96..aa8f1cb 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,18 +1,32 @@ import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; +import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; +import { CartModule } from './cart/cart.module'; + import { AppComponent } from './app.component'; +import { ModulesComponent } from './modules/modules.component'; +import { ModulesItemComponent } from './modules-item/modules-item.component'; + +import { HomeComponent } from './home/home.component'; +import { ModuleDetailComponent } from './module-detail/module-detail.component'; + @NgModule({ declarations: [ - AppComponent + AppComponent, + ModulesComponent, + HomeComponent, + ModuleDetailComponent, + ModulesItemComponent ], imports: [ BrowserModule, - AppRoutingModule + AppRoutingModule, + CartModule, ], providers: [], bootstrap: [AppComponent] }) + export class AppModule { } diff --git a/src/app/cart-item/cart-item.component.css b/src/app/cart-item/cart-item.component.css new file mode 100644 index 0000000..5a8f7ac --- /dev/null +++ b/src/app/cart-item/cart-item.component.css @@ -0,0 +1,29 @@ +.cluster { + margin-left: 10px; + color: #222; + font-size: 0.8rem; +} + +.item-name { + margin: 0.5rem 0; +} + +.item-actions { + display: flex; + justify-content: space-between; + align-items: center; + margin-left: 0.5rem; +} + +.item-actions button { + height: 1.5rem; + width: 1.5rem; + margin-left: 0.15rem; +} + +.item { + display: flex; + justify-content: space-between; + align-items: center; +} + diff --git a/src/app/cart-item/cart-item.component.html b/src/app/cart-item/cart-item.component.html new file mode 100644 index 0000000..c3a882b --- /dev/null +++ b/src/app/cart-item/cart-item.component.html @@ -0,0 +1,18 @@ +
+
+
{{item.name}}
+
{{item.cluster}}
+
+ +
+ + + +
+
diff --git a/src/app/cart-item/cart-item.component.ts b/src/app/cart-item/cart-item.component.ts new file mode 100644 index 0000000..9172859 --- /dev/null +++ b/src/app/cart-item/cart-item.component.ts @@ -0,0 +1,37 @@ +import { Component, Input } from '@angular/core'; +import { Module } from '../interfaces/module'; +import { CartService } from '../cart/cart.service'; + +@Component({ + selector: 'app-cart-item', + templateUrl: './cart-item.component.html', + styleUrls: ['./cart-item.component.css'] +}) + + +export class CartItemComponent { + @Input() item?: Module; + + constructor(private cart: CartService) { } + + removeFromCart(module: Module): void { + this.cart.remove(module); + } + + isFirst(module: Module): boolean { + return this.cart.isFirst(module); + } + + isLast(module: Module): boolean { + return this.cart.isLast(module); + } + + moveUp(module: Module): void { + this.cart.moveUp(module); + } + + moveDown(module: Module): void { + this.cart.moveDown(module); + } +} + diff --git a/src/app/cart/cart.component.css b/src/app/cart/cart.component.css new file mode 100644 index 0000000..eb3e326 --- /dev/null +++ b/src/app/cart/cart.component.css @@ -0,0 +1,38 @@ +.cart { + position: fixed; + bottom: 0; + right: 0; + background-color: #ccc; + padding: 1rem; +} + +.headline { + cursor: pointer; + text-transform: uppercase; +} + +.cart-content.hidden { + max-height: 0; +} + +.cart-content { + transition: max-height 0.3s ease-out; + max-height: 380px; + overflow: hidden; +} + +.cart-items { + overflow: auto; + max-height: 300px; + padding: 0 0.5rem; +} + +.cart-actions { + display: flex; + width: 100%; + justify-content: flex-end; +} + +.cart-actions button { + margin-left: 0.2rem; +} diff --git a/src/app/cart/cart.component.html b/src/app/cart/cart.component.html new file mode 100644 index 0000000..a04b2ad --- /dev/null +++ b/src/app/cart/cart.component.html @@ -0,0 +1,13 @@ +
+

Konfiguration ({{items.length}}) {{ isOpen ? '🡇' : '🡅' }}

+
+
+ +
+
+ +
+
+
diff --git a/src/app/cart/cart.component.ts b/src/app/cart/cart.component.ts new file mode 100644 index 0000000..00868a8 --- /dev/null +++ b/src/app/cart/cart.component.ts @@ -0,0 +1,44 @@ +import { Component } from '@angular/core'; +import { CartService } from './cart.service'; +import { ApiService } from '../api.service'; +import { Module } from '../interfaces/module'; + +@Component({ + selector: 'app-cart', + templateUrl: './cart.component.html', + styleUrls: ['./cart.component.css'] +}) + +export class CartComponent { + items = this.cart.items; + availableModules: Module[] = []; + isOpen = true; + + constructor( + private cart: CartService, + private api: ApiService + ) { } + + ngOnInit(): void { + this.api.getModules().subscribe(allModules => { + this.availableModules = allModules; + }); + } + + toggle(): void { + this.isOpen = !this.isOpen; + } + + hasAllClusters(): boolean { + return this.cart.hasAllClusters(); + } + + makeOrder(): void { + try { + const orderedModules = this.cart.order(); + console.log(orderedModules); + } catch (error) { + console.log(error); + } + } +} diff --git a/src/app/cart/cart.module.ts b/src/app/cart/cart.module.ts new file mode 100644 index 0000000..b20e1a1 --- /dev/null +++ b/src/app/cart/cart.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { CartComponent } from './cart.component'; +import { CartItemComponent } from '../cart-item/cart-item.component'; + +@NgModule({ + imports: [ + CommonModule + ], + declarations: [ + CartComponent, + CartItemComponent, + ], + exports: [ + CartComponent, + ], +}) + +export class CartModule { } diff --git a/src/app/cart/cart.service.spec.ts b/src/app/cart/cart.service.spec.ts new file mode 100644 index 0000000..1d0fd10 --- /dev/null +++ b/src/app/cart/cart.service.spec.ts @@ -0,0 +1,190 @@ +import { TestBed } from '@angular/core/testing'; + +import { CartService } from './cart.service'; + +import { Module, Cluster } from '../interfaces/module'; + +describe('CartService', () => { + let service: CartService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(CartService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should add module to cart', () => { + const mod: Module = { + id: 1, + name: 'Module 1', + cluster: 'FMEA', + description: 'Description 1' + }; + + service.add(mod); + expect(service.items).toContain(mod); + }); + + it('should not add the same module to cart twice', () => { + const mod: Module = { + id: 1, + name: 'Module 1', + cluster: 'FMEA', + description: 'Description 1' + }; + + service.add(mod); + service.add(mod); + + expect(service.items).toContain(mod); + expect(service.items.length).toBe(1); + }); + + it('should throw error when trying to order without all clusters', () => { + const mod1: Module = { + id: 1, + name: 'Module 1', + cluster: 'FMEA', + description: 'Description 1' + }; + + const mod2: Module = { + id: 2, + name: 'Module 2', + cluster: 'FMEA', + description: 'Description 2' + }; + + service.add(mod1); + service.add(mod2); + + expect(() => service.order()).toThrowError('Not all clusters are selected'); + }); + + it('should return all ordered modules if all clusters are selected', () => { + const clusters: Cluster[] = ['FMEA', 'Control-Plan', 'Fertigungsprüfung', 'Reklamationsmanagement', 'Maßnahmenmanagement']; + + clusters.forEach((cluster, idx) => { + let mod: Module = { + id: idx, + name: `Module ${idx}`, + cluster, + description: 'Description 1' + }; + + service.add(mod); + }); + + expect(service.order().length).toBe(5); + }); + + it('should remove module from cart', () => { + const mod: Module = { + id: 1, + name: 'Module 1', + cluster: 'FMEA', + description: 'Description 1' + }; + + service.add(mod); + service.remove(mod); + + expect(service.items).not.toContain(mod); + expect(service.items.length).toBe(0); + }); + + it('should move module up', () => { + const mod1: Module = { + id: 1, + name: 'Module 1', + cluster: 'FMEA', + description: 'Description 1' + }; + + const mod2: Module = { + id: 2, + name: 'Module 2', + cluster: 'FMEA', + description: 'Description 2' + }; + + service.add(mod1); + service.add(mod2); + + service.moveUp(mod2); + + expect(service.items[0]).toBe(mod2); + expect(service.items[1]).toBe(mod1); + }); + + it('should move module down', () => { + const mod1: Module = { + id: 1, + name: 'Module 1', + cluster: 'FMEA', + description: 'Description 1' + }; + + const mod2: Module = { + id: 2, + name: 'Module 2', + cluster: 'FMEA', + description: 'Description 2' + }; + + service.add(mod1); + service.add(mod2); + + service.moveDown(mod1); + + expect(service.items[0]).toBe(mod2); + expect(service.items[1]).toBe(mod1); + }); + + it('should return true if module is first', () => { + const mod1: Module = { + id: 1, + name: 'Module 1', + cluster: 'FMEA', + description: 'Description 1' + }; + + const mod2: Module = { + id: 2, + name: 'Module 2', + cluster: 'FMEA', + description: 'Description 2' + }; + + service.add(mod1); + service.add(mod2); + + expect(service.isFirst(mod1)).toBe(true); + expect(service.isFirst(mod2)).toBe(false); + }); + + it('should return true if module is last', () => { + const mod1: Module = { + id: 1, + name: 'Module 1', + cluster: 'FMEA', + description: 'Description 1' + }; + + const mod2: Module = { + id: 2, + name: 'Module 2', + cluster: 'FMEA', + description: 'Description 2' + }; + + service.add(mod1); + service.add(mod2); + + expect(service.isLast(mod1)).toBe(false); + expect(service.isLast(mod2)).toBe(true); + }); +}); diff --git a/src/app/cart/cart.service.ts b/src/app/cart/cart.service.ts new file mode 100644 index 0000000..2a928b5 --- /dev/null +++ b/src/app/cart/cart.service.ts @@ -0,0 +1,75 @@ +import { Injectable } from '@angular/core'; +import { Module } from '../interfaces/module'; +import { ApiService } from '../api.service'; + +@Injectable({ + providedIn: 'root' +}) + +export class CartService { + items: Module[] = []; + numberAllClusters: number = 0; + + constructor(private api: ApiService) { + this.api.getModules().subscribe(allModules => { + this.numberAllClusters = [...new Set(allModules.map(module => module.cluster))].length; + }) + } + + add(module: Module): void { + if (!this.isInCart(module)) { + this.items.push(module); + } + } + + remove(module: Module): void { + const index = this.items.indexOf(module); + if (index > -1) { + this.items.splice(index, 1); + } + } + + isInCart(module: Module): boolean { + return this.items.includes(module); + } + + hasAllClusters(): boolean { + let clusters = [...new Set(this.items.map(module => module.cluster))]; + + return clusters.length === this.numberAllClusters; + } + + isFirst(module: Module): boolean { + return this.items.indexOf(module) === 0; + } + + isLast(module: Module): boolean { + return this.items.indexOf(module) === this.items.length - 1; + } + + moveUp(module: Module): void { + const index = this.items.indexOf(module); + + if (index > 0) { + this.items.splice(index, 1); + this.items.splice(index - 1, 0, module); + } + } + + moveDown(module: Module): void { + const index = this.items.indexOf(module); + + if (index < this.items.length - 1) { + this.items.splice(index, 1); + this.items.splice(index + 1, 0, module); + } + } + + order(): Module[] { + if (!this.hasAllClusters()) { + throw new Error('Not all clusters are selected'); + } + + return this.api.orderModules(this.items); + } +} diff --git a/src/app/home/home.component.css b/src/app/home/home.component.css new file mode 100644 index 0000000..8a86e7c --- /dev/null +++ b/src/app/home/home.component.css @@ -0,0 +1,4 @@ +div { + padding: 0.5rem 2rem; +} + diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html new file mode 100644 index 0000000..8edf352 --- /dev/null +++ b/src/app/home/home.component.html @@ -0,0 +1,8 @@ +
+

+ Hier können Sie eine individuelle Modulkonfiguration vornehmen. +

+

+ Sie müssen mindestens ein Modul aus jedem Cluster ausgewählt haben, bevor eine Angebotsanfrage verschickt werden kann. +

+
diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts new file mode 100644 index 0000000..a9f7f67 --- /dev/null +++ b/src/app/home/home.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.css'] +}) + +export class HomeComponent { } diff --git a/src/app/interfaces/module.ts b/src/app/interfaces/module.ts new file mode 100644 index 0000000..5bc0a0e --- /dev/null +++ b/src/app/interfaces/module.ts @@ -0,0 +1,8 @@ +export type Cluster = 'FMEA'|'Control-Plan'|'Fertigungsprüfung'|'Reklamationsmanagement'|'Maßnahmenmanagement' + +export type Module = { + id: number + name: string + cluster: Cluster + description: string +} diff --git a/src/app/mocks/modules.ts b/src/app/mocks/modules.ts new file mode 100644 index 0000000..ea15be9 --- /dev/null +++ b/src/app/mocks/modules.ts @@ -0,0 +1,124 @@ +import { Module } from '../interfaces/module'; + +export const MODULES: Module[] = [ + { + id: 1, + name: 'Musterprüfung', + cluster: 'Control-Plan', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 2, + name: 'Erstbemusterung', + cluster: 'Control-Plan', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + + { + id: 3, + name: 'CAD Integration', + cluster: 'Fertigungsprüfung', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 4, + name: 'Vorbeugende Instandhaltung', + cluster: 'Fertigungsprüfung', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 5, + name: 'Wareneingangs- / Warenausgangsprüfung', + cluster: 'Fertigungsprüfung', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 6, + name: 'Prüfmittelmanagement', + cluster: 'Fertigungsprüfung', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 7, + name: 'Prüfmittelfähigkeitsuntersuchung', + cluster: 'Fertigungsprüfung', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + + { + id: 8, + name: 'Warranty Management', + cluster: 'Reklamationsmanagement', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 9, + name: 'CAPA Management', + cluster: 'Reklamationsmanagement', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + + { + id: 10, + name: 'Quality Cockpit', + cluster: 'Maßnahmenmanagement', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 11, + name: 'Schulungsmanagement', + cluster: 'Maßnahmenmanagement', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 12, + name: 'Qualifikationsmanagement', + cluster: 'Maßnahmenmanagement', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 13, + name: 'Aufgabenmanagement', + cluster: 'Maßnahmenmanagement', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 14, + name: 'Checklisten', + cluster: 'Maßnahmenmanagement', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 15, + name: 'Lieferanten Cockpit', + cluster: 'Maßnahmenmanagement', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 16, + name: 'Auditmanagement', + cluster: 'Maßnahmenmanagement', + description: 'Unternehmen nutzen Audits, um im eigenen Haus oder bei Lieferanten die Wirksamkeit eines Managementsystems zu überprüfen und sicherzustellen. Es wird untersucht, ob die unternehmensinternen Prozesse sowie die Anforderungen und Richtlinien den Standards entsprechen. So kann nicht nur der Ist-Zustand analysiert, sondern Verbesserungspotenzial effektiv aufgedeckt werden.', + }, + { + id: 17, + name: 'Prozessmanagement', + cluster: 'Maßnahmenmanagement', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + { + id: 18, + name: 'Dokumentenlenkung', + cluster: 'Maßnahmenmanagement', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, + + { + id: 19, + name: 'APQP Projektmanagement', + cluster: 'FMEA', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla euismod, nisl quis tincidunt ultricies, nunc nisl ultricies nunc, quis ultricies ni', + }, +] + + diff --git a/src/app/module-detail/module-detail.component.css b/src/app/module-detail/module-detail.component.css new file mode 100644 index 0000000..61622c2 --- /dev/null +++ b/src/app/module-detail/module-detail.component.css @@ -0,0 +1,7 @@ +.module-detail { + padding: 0.5rem 2rem; +} + +button { + margin-top: 1rem; +} diff --git a/src/app/module-detail/module-detail.component.html b/src/app/module-detail/module-detail.component.html new file mode 100644 index 0000000..8975d5f --- /dev/null +++ b/src/app/module-detail/module-detail.component.html @@ -0,0 +1,19 @@ +
+

{{module.name}}

+
+ {{ module.description }} +
+ + +
+ +
+

Kein Modul ausgewählt oder nicht gefunden

+
+ diff --git a/src/app/module-detail/module-detail.component.ts b/src/app/module-detail/module-detail.component.ts new file mode 100644 index 0000000..cd24f64 --- /dev/null +++ b/src/app/module-detail/module-detail.component.ts @@ -0,0 +1,69 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Location } from '@angular/common'; + +import { Module } from '../interfaces/module'; + +import { ApiService } from '../api.service'; +import { CartService } from '../cart/cart.service'; + +@Component({ + selector: 'app-module-detail', + templateUrl: './module-detail.component.html', + styleUrls: ['./module-detail.component.css'] +}) + +export class ModuleDetailComponent implements OnInit { + module: Module | undefined; + + constructor( + private route: ActivatedRoute, + private api: ApiService, + private location: Location, + private cart: CartService + ) {} + + ngOnInit(): void { + this.getModule(); + } + + getModule(): void { + this.route.params.subscribe((params) => { + if (params?.['id']) { + let id = Number(params['id']); + this.api.getModule(id).subscribe(module => { + if (!module) { + this.location.go('/'); + return; + } + + this.module = module; + }); + } + }); + } + + isInCart(): boolean { + if (!this.module) { + return false; + } + + return this.cart.isInCart(this.module); + } + + addToCart(): void { + if (!this.module) { + return; + } + + this.cart.add(this.module); + } + + removeFromCart(): void { + if (!this.module) { + return; + } + + this.cart.remove(this.module); + } +} diff --git a/src/app/modules-item/modules-item.component.css b/src/app/modules-item/modules-item.component.css new file mode 100644 index 0000000..8f459c9 --- /dev/null +++ b/src/app/modules-item/modules-item.component.css @@ -0,0 +1,22 @@ +.module { + display: flex; + justify-content: space-between; + width: 100%; + margin: 0.1rem 0; + align-items: center; + height: 2rem; +} + +button.add span { + display: inline-block; + transform: rotate(45deg); +} + +button.remove { + background-color: rgba(255, 0, 0, 0.1); +} + +button { + margin-left: 0.5rem; + border: 1px solid #ccc; +} diff --git a/src/app/modules-item/modules-item.component.html b/src/app/modules-item/modules-item.component.html new file mode 100644 index 0000000..27b225b --- /dev/null +++ b/src/app/modules-item/modules-item.component.html @@ -0,0 +1,19 @@ +
+ + {{module.name}} + + + + +
diff --git a/src/app/modules-item/modules-item.component.ts b/src/app/modules-item/modules-item.component.ts new file mode 100644 index 0000000..0b8c2cb --- /dev/null +++ b/src/app/modules-item/modules-item.component.ts @@ -0,0 +1,27 @@ +import { Component, Input } from '@angular/core'; +import { Module } from '../interfaces/module'; +import { CartService } from '../cart/cart.service'; + +@Component({ + selector: 'app-modules-item', + templateUrl: './modules-item.component.html', + styleUrls: ['./modules-item.component.css'] +}) + +export class ModulesItemComponent { + @Input() module?: Module; + + constructor(private cart: CartService) { } + + addToCart(module: Module): void { + this.cart.add(module); + } + + removeFromCart(module: Module): void { + this.cart.remove(module); + } + + isInCart(module: Module): boolean { + return this.cart.isInCart(module); + } +} diff --git a/src/app/modules/modules.component.css b/src/app/modules/modules.component.css new file mode 100644 index 0000000..f96b0e5 --- /dev/null +++ b/src/app/modules/modules.component.css @@ -0,0 +1,36 @@ +.module.hidden { + display: none; +} + +summary { + cursor: pointer; +} + +summary, .summary { + padding: 0.5rem 0.5rem 0.5rem 0; +} + +details > summary { + list-style: none; +} +details > summary::-webkit-details-marker { + display: none; +} + +summary:after { + content: "►"; +} + +details[open] summary:after { + content: "▼"; +} + +summary * { + display: inline; +} + +h4 { + margin: 0; + width: 100%; +} + diff --git a/src/app/modules/modules.component.html b/src/app/modules/modules.component.html new file mode 100644 index 0000000..1c9f2ae --- /dev/null +++ b/src/app/modules/modules.component.html @@ -0,0 +1,22 @@ +
+
+

Alle Module

+
+ + +
+ +
+
+

{{cluster}}

+ + +
+
diff --git a/src/app/modules/modules.component.ts b/src/app/modules/modules.component.ts new file mode 100644 index 0000000..dfdff8d --- /dev/null +++ b/src/app/modules/modules.component.ts @@ -0,0 +1,36 @@ +import { Component, Input } from '@angular/core'; +import { Module } from '../interfaces/module'; +import { ApiService } from '../api.service'; +import { CartService } from '../cart/cart.service'; + +@Component({ + selector: 'app-modules', + templateUrl: './modules.component.html', + styleUrls: ['./modules.component.css'] +}) + +export class ModulesComponent { + @Input() style?: string = 'all'; + clusters: string[] = []; + modules: Module[] = []; + + constructor( + private api: ApiService, + private cart: CartService + ) { } + + addToCart(module: Module): void { + this.cart.add(module); + } + + initializeModules(): void { + this.api.getModules().subscribe(allModules => { + this.modules = allModules.sort((a, b) => a.name.localeCompare(b.name)); + this.clusters = [...new Set(allModules.map(module => module.cluster))].sort(); + }); + } + + ngOnInit(): void { + this.initializeModules(); + } +} diff --git a/src/index.html b/src/index.html index 43e302f..ca13e81 100644 --- a/src/index.html +++ b/src/index.html @@ -2,7 +2,7 @@ - ModuleConfigurator + Modul Konfigurator diff --git a/src/styles.css b/src/styles.css index 90d4ee0..e8a1499 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1 +1,4 @@ /* You can add global styles to this file, and also import other style files */ +h1, h2, h3, h4, h5, h6 { + margin: 8px 0; +}