change repo
This commit is contained in:
16
.editorconfig
Normal file
16
.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Editor configuration, see https://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.ts]
|
||||||
|
quote_type = single
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
||||||
4
.vscode/extensions.json
vendored
Normal file
4
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||||
|
"recommendations": ["angular.ng-template"]
|
||||||
|
}
|
||||||
20
.vscode/launch.json
vendored
Normal file
20
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "ng serve",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "npm: start",
|
||||||
|
"url": "http://localhost:4200/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng test",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "npm: test",
|
||||||
|
"url": "http://localhost:9876/debug.html"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
42
.vscode/tasks.json
vendored
Normal file
42
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "start",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "typescript",
|
||||||
|
"pattern": "$tsc",
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": {
|
||||||
|
"regexp": "(.*?)"
|
||||||
|
},
|
||||||
|
"endsPattern": {
|
||||||
|
"regexp": "bundle generation complete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "test",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "typescript",
|
||||||
|
"pattern": "$tsc",
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": {
|
||||||
|
"regexp": "(.*?)"
|
||||||
|
},
|
||||||
|
"endsPattern": {
|
||||||
|
"regexp": "bundle generation complete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
27
README.md
Normal file
27
README.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# LLCE
|
||||||
|
|
||||||
|
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.5.
|
||||||
|
|
||||||
|
## Development server
|
||||||
|
|
||||||
|
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
|
||||||
|
|
||||||
|
## Code scaffolding
|
||||||
|
|
||||||
|
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||||
|
|
||||||
|
## Running end-to-end tests
|
||||||
|
|
||||||
|
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||||
|
|
||||||
|
## Further help
|
||||||
|
|
||||||
|
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||||
21
SECURITY.md
Normal file
21
SECURITY.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Use this section to tell people about which versions of your project are
|
||||||
|
currently being supported with security updates.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 5.1.x | :white_check_mark: |
|
||||||
|
| 5.0.x | :x: |
|
||||||
|
| 4.0.x | :white_check_mark: |
|
||||||
|
| < 4.0 | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Use this section to tell people how to report a vulnerability.
|
||||||
|
|
||||||
|
Tell them where to go, how often they can expect to get an update on a
|
||||||
|
reported vulnerability, what to expect if the vulnerability is accepted or
|
||||||
|
declined, etc.
|
||||||
102
angular.json
Normal file
102
angular.json
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"LLCE": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {},
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"prefix": "LL",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/llce",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"polyfills": [
|
||||||
|
"zone.js"
|
||||||
|
],
|
||||||
|
"tsConfig": "tsconfig.app.json",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/custom-theme.scss",
|
||||||
|
"src/styles.css"
|
||||||
|
],
|
||||||
|
"scripts": []
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "500kb",
|
||||||
|
"maximumError": "1mb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "anyComponentStyle",
|
||||||
|
"maximumWarning": "2kb",
|
||||||
|
"maximumError": "4kb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputHashing": "all"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildOptimizer": false,
|
||||||
|
"optimization": false,
|
||||||
|
"vendorChunk": true,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"namedChunks": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "LLCE:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"browserTarget": "LLCE:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development"
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "LLCE:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"polyfills": [
|
||||||
|
"zone.js",
|
||||||
|
"zone.js/testing"
|
||||||
|
],
|
||||||
|
"tsConfig": "tsconfig.spec.json",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/styles.css"
|
||||||
|
],
|
||||||
|
"scripts": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cli": {
|
||||||
|
"analytics": false
|
||||||
|
}
|
||||||
|
}
|
||||||
13119
package-lock.json
generated
Normal file
13119
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
41
package.json
Normal file
41
package.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "llce",
|
||||||
|
"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/animations": "^16.1.0",
|
||||||
|
"@angular/cdk": "^16.2.1",
|
||||||
|
"@angular/common": "^16.1.0",
|
||||||
|
"@angular/compiler": "^16.1.0",
|
||||||
|
"@angular/core": "^16.1.0",
|
||||||
|
"@angular/forms": "^16.1.0",
|
||||||
|
"@angular/material": "^16.2.1",
|
||||||
|
"@angular/platform-browser": "^16.1.0",
|
||||||
|
"@angular/platform-browser-dynamic": "^16.1.0",
|
||||||
|
"@angular/router": "^16.1.0",
|
||||||
|
"ngx-cookie-service": "^16.0.1",
|
||||||
|
"rxjs": "~7.8.0",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"zone.js": "~0.13.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular-devkit/build-angular": "^16.1.5",
|
||||||
|
"@angular/cli": "~16.1.5",
|
||||||
|
"@angular/compiler-cli": "^16.1.0",
|
||||||
|
"@types/jasmine": "~4.3.0",
|
||||||
|
"jasmine-core": "~4.6.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.1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/app/Model/answer-data.ts
Normal file
7
src/app/Model/answer-data.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Question } from "./question";
|
||||||
|
|
||||||
|
export interface AnswerData {
|
||||||
|
question: Question;
|
||||||
|
userAnswer: string;
|
||||||
|
isCorrect: boolean;
|
||||||
|
}
|
||||||
6
src/app/Model/answers.ts
Normal file
6
src/app/Model/answers.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { FrageOption } from "./frage-option";
|
||||||
|
|
||||||
|
export interface Answers {
|
||||||
|
selectedChoice: FrageOption[]
|
||||||
|
userAnswer: string;
|
||||||
|
}
|
||||||
4
src/app/Model/antwort-option.ts
Normal file
4
src/app/Model/antwort-option.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface AntwortOption {
|
||||||
|
text: string;
|
||||||
|
selected: boolean;
|
||||||
|
}
|
||||||
4
src/app/Model/frage-option.ts
Normal file
4
src/app/Model/frage-option.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface FrageOption {
|
||||||
|
text: string;
|
||||||
|
selected: boolean;
|
||||||
|
}
|
||||||
9
src/app/Model/question.ts
Normal file
9
src/app/Model/question.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { FrageOption } from "./frage-option";
|
||||||
|
|
||||||
|
export interface Question {
|
||||||
|
questionNumber: string;
|
||||||
|
questionText: string;
|
||||||
|
choices: FrageOption[];
|
||||||
|
answer: string | string[];
|
||||||
|
questionType: 'single' | 'multiple' | 'input';
|
||||||
|
}
|
||||||
8
src/app/Popup/error-message/error-message.component.html
Normal file
8
src/app/Popup/error-message/error-message.component.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<h2 mat-dialog-title>Statistic</h2>
|
||||||
|
<mat-dialog-content>
|
||||||
|
<div [innerHTML]="formattedMessage"></div>
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<button mat-button (click)="errorPopup()">OK</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
|
|
||||||
20
src/app/Popup/error-message/error-message.component.ts
Normal file
20
src/app/Popup/error-message/error-message.component.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-error-message',
|
||||||
|
templateUrl: './error-message.component.html',
|
||||||
|
styleUrls: ['./error-message.component.css'],
|
||||||
|
})
|
||||||
|
export class ErrorMessageComponent {
|
||||||
|
formattedMessage: string;
|
||||||
|
constructor(
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { message: string },
|
||||||
|
public dialogRef: MatDialogRef<ErrorMessageComponent>
|
||||||
|
) {
|
||||||
|
this.formattedMessage = data.message.replace(/\n/g, '<br>');
|
||||||
|
}
|
||||||
|
errorPopup(): void {
|
||||||
|
window.location.href = '/home';
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/app/Popup/pop-up/pop-up.component.css
Normal file
19
src/app/Popup/pop-up/pop-up.component.css
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
.errorpopup {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorpopup-content {
|
||||||
|
background-color: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
9
src/app/Popup/pop-up/pop-up.component.html
Normal file
9
src/app/Popup/pop-up/pop-up.component.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
<h2 mat-dialog-title>Warning</h2>
|
||||||
|
<mat-dialog-content>
|
||||||
|
<div [innerHTML]="formattedMessage"></div>
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<button mat-button (click)="closePopUp()">OK</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
|
|
||||||
21
src/app/Popup/pop-up/pop-up.component.ts
Normal file
21
src/app/Popup/pop-up/pop-up.component.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-pop-up',
|
||||||
|
templateUrl: './pop-up.component.html',
|
||||||
|
styleUrls: ['./pop-up.component.css'],
|
||||||
|
})
|
||||||
|
export class PopUpComponent {
|
||||||
|
formattedMessage: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { message: string },
|
||||||
|
public dialogRef: MatDialogRef<PopUpComponent>
|
||||||
|
) {
|
||||||
|
this.formattedMessage = data.message.replace(/\n/g, '<br>');
|
||||||
|
}
|
||||||
|
closePopUp(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
0
src/app/Popup/statistik/statistik.component.css
Normal file
0
src/app/Popup/statistik/statistik.component.css
Normal file
8
src/app/Popup/statistik/statistik.component.html
Normal file
8
src/app/Popup/statistik/statistik.component.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<h2 mat-dialog-title>Statistic</h2>
|
||||||
|
<mat-dialog-content>
|
||||||
|
<div [innerHTML]="formattedMessage"></div>
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<button mat-button (click)="openStatisticPopup()">OK</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
|
|
||||||
20
src/app/Popup/statistik/statistik.component.ts
Normal file
20
src/app/Popup/statistik/statistik.component.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-statistik',
|
||||||
|
templateUrl: './statistik.component.html',
|
||||||
|
styleUrls: ['./statistik.component.css'],
|
||||||
|
})
|
||||||
|
export class StatistikComponent {
|
||||||
|
formattedMessage: string;
|
||||||
|
constructor(
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { message: string },
|
||||||
|
public dialogRef: MatDialogRef<StatistikComponent>
|
||||||
|
) {
|
||||||
|
this.formattedMessage = data.message.replace(/\n/g, '<br>');
|
||||||
|
}
|
||||||
|
openStatisticPopup(): void {
|
||||||
|
window.location.href = '/home';
|
||||||
|
}
|
||||||
|
}
|
||||||
0
src/app/Templates/filter/filter.component.css
Normal file
0
src/app/Templates/filter/filter.component.css
Normal file
37
src/app/Templates/filter/filter.component.html
Normal file
37
src/app/Templates/filter/filter.component.html
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<div class="questions-section">
|
||||||
|
<div class="question-type-buttons">
|
||||||
|
|
||||||
|
<button class="show-answer-button" *ngFor="let type of questionTypes" [class.selected]="selectedKatalog === type" (click)="onTypeButtonClick(type)">{{ type }}</button>
|
||||||
|
|
||||||
|
<div class="button-container">
|
||||||
|
<button class="show-answer-button" [class.selected]="selectedKatalog === 'LPI101'" (click)="selectCatalog('LPI101')">Katalog 1</button>
|
||||||
|
|
||||||
|
<button class="show-answer-button" [class.selected]="selectedKatalog === 'LPI102'"(click)="selectCatalog('LPI102')">Katalog 2</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="question-container">
|
||||||
|
<div class="question-block" *ngFor="let question of filteredQuestions; let i = index">
|
||||||
|
<h3>Question {{i+1}}</h3>
|
||||||
|
<div class="question-text">{{question.questionText}}</div>
|
||||||
|
|
||||||
|
<div class="choice-text">
|
||||||
|
<ng-container *ngIf="question.questionType === 'single'">
|
||||||
|
<LL-single-choice-question [choices]="question.choices"></LL-single-choice-question>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="question.questionType === 'multiple'">
|
||||||
|
<LL-multiple-choice-question [choices]="question.choices"></LL-multiple-choice-question>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="question.questionType === 'input'">
|
||||||
|
<LL-input-choice-question
|
||||||
|
[userAnswer]="getTrueFalse().getCurrentAnswers().userAnswer"></LL-input-choice-question>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
87
src/app/Templates/filter/filter.component.ts
Normal file
87
src/app/Templates/filter/filter.component.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { Question } from 'src/app/Model/question';
|
||||||
|
import { FilterService } from 'src/app/services/filter.service';
|
||||||
|
import { QuestionNavigationService } from 'src/app/services/question-navigation.service';
|
||||||
|
import { QuestionToggleService } from 'src/app/services/question-toggle.service';
|
||||||
|
import { TrueOrFalesService } from 'src/app/services/true-or-false.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-filter',
|
||||||
|
templateUrl: './filter.component.html',
|
||||||
|
styleUrls: ['./filter.component.css']
|
||||||
|
})
|
||||||
|
export class FilterComponent {
|
||||||
|
@Input() selectedType: string = '';
|
||||||
|
@Output() selectedTypeChange = new EventEmitter<string>();
|
||||||
|
|
||||||
|
filteredQuestions: Question[] = [];
|
||||||
|
selectedKatalog: string = 'LPI101';
|
||||||
|
questionTypes: string[] = ['single', 'multiple', 'input'];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private trueFalse: TrueOrFalesService,
|
||||||
|
private filterService: FilterService,
|
||||||
|
private questNav: QuestionNavigationService,
|
||||||
|
private questService: QuestionToggleService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.route.params.subscribe((params) => {
|
||||||
|
const katalog = params['katalog'];
|
||||||
|
if (katalog === 'LPI101') {
|
||||||
|
this.trueFalse.loadKatalogAndQuestions().subscribe(() => {});
|
||||||
|
} else if (katalog === 'LPI102') {
|
||||||
|
this.trueFalse.loadKatalog2AndQuestions().subscribe(() => {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.filterService.selectedType$.subscribe(selectedType => {
|
||||||
|
this.filterQuestions(selectedType);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
filterQuestions(selectedType: string): void {
|
||||||
|
if (this.selectedKatalog === 'LPI101') {
|
||||||
|
this.filteredQuestions = this.filterService.filteredQuestionsLPI101;
|
||||||
|
} else if (this.selectedKatalog === 'LPI102') {
|
||||||
|
this.filteredQuestions = this.filterService.filteredQuestionsLPI102;
|
||||||
|
}
|
||||||
|
console.log('fSfQL1',this.filterService.filteredQuestionsLPI101,'fQ',this.filteredQuestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
selectCatalog(katalog: string): void{
|
||||||
|
this.selectedKatalog = katalog;
|
||||||
|
this.filterQuestions(this.selectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
onTypeButtonClick(type: string): void {
|
||||||
|
this.selectedType = type;
|
||||||
|
this.filterService.setSelectedType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
onTypeChange(event: any): void {
|
||||||
|
const selectedType = event?.target?.value;
|
||||||
|
if (selectedType !== undefined) {
|
||||||
|
this.selectedTypeChange.emit(selectedType);
|
||||||
|
this.filterService.setSelectedType(selectedType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKatalogChange(selectedKatalog: string): void {
|
||||||
|
this.selectedKatalog = selectedKatalog;
|
||||||
|
this.filterQuestions(this.selectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
getQuestionNav(): QuestionNavigationService {
|
||||||
|
return this.questNav;
|
||||||
|
}
|
||||||
|
getTrueFalse(): TrueOrFalesService {
|
||||||
|
return this.trueFalse;
|
||||||
|
}
|
||||||
|
getQuestionServ(): QuestionToggleService{
|
||||||
|
return this.questService;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<!-- InputFragen / Fillin -->
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<div>Your answer:</div>
|
||||||
|
<input type="text" [(ngModel)]="inputUserAnswer" (ngModelChange)="onInputUserAnswerChange()" placeholder="Please enter your text" name="userAnswer">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Component, EventEmitter, Input, Output, SimpleChanges } from '@angular/core';
|
||||||
|
import { QuestionToggleService } from '../../services/question-toggle.service';
|
||||||
|
import { TrueOrFalesService } from '../../services/true-or-false.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-input-choice-question',
|
||||||
|
templateUrl: './input-choice-questeion.component.html',
|
||||||
|
styleUrls: ['./input-choice-questeion.component.css']
|
||||||
|
})
|
||||||
|
export class InputChoiceQuesteionComponent {
|
||||||
|
@Input() userAnswer: string | undefined;
|
||||||
|
@Output() userAnswerChange = new EventEmitter<string>();
|
||||||
|
|
||||||
|
inputUserAnswer: string = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private trueFalse: TrueOrFalesService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void{
|
||||||
|
if(changes['userAnswer']){
|
||||||
|
this.inputUserAnswer = this.userAnswer || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onInputUserAnswerChange(): void{
|
||||||
|
const currentAnswers = this.trueFalse.getCurrentAnswers();
|
||||||
|
currentAnswers.userAnswer = this.inputUserAnswer || '';
|
||||||
|
this.trueFalse.updateCurrentAnswers(currentAnswers);
|
||||||
|
|
||||||
|
this.userAnswerChange.emit(this.inputUserAnswer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<!-- MultipleChoice -->
|
||||||
|
<div *ngFor="let choice of choices; index as k">
|
||||||
|
<div *ngIf="!choice.selected">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" (click)="toggleChoiceSelection(choice)" name="choices">
|
||||||
|
{{ choice.text }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="choice.selected">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" (click)="toggleChoiceSelection(choice)" name="choices" checked>
|
||||||
|
{{ choice.text }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { FrageOption } from '../../Model/frage-option';
|
||||||
|
import { QuestionToggleService } from '../../services/question-toggle.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-multiple-choice-question',
|
||||||
|
templateUrl: './multiple-choice-questeion.component.html',
|
||||||
|
styleUrls: ['./multiple-choice-questeion.component.css']
|
||||||
|
})
|
||||||
|
export class MultipleChoiceQuestionComponent {
|
||||||
|
@Input() choices: FrageOption[] | undefined;
|
||||||
|
@Output() choiceSelectionToggle = new EventEmitter<FrageOption>();
|
||||||
|
|
||||||
|
constructor(private questService: QuestionToggleService) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleChoiceSelection(choice: FrageOption): void{
|
||||||
|
this.questService.toggleChoiceSelectionMulti(choice);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<div>
|
||||||
|
<button class="show-answer-button" (click)="changeLanguage('german')">German</button>
|
||||||
|
<button class="show-answer-button" (click)="changeLanguage('english')">English</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>{{privacyPolicy[currentLanguage].introduction}}</h2>
|
||||||
|
<p>{{privacyPolicy[currentLanguage].dataController}}</p>
|
||||||
|
<p>{{privacyPolicy[currentLanguage].dataTypes}}</p>
|
||||||
|
<p>{{privacyPolicy[currentLanguage].dataProcessingPurpose}}</p>
|
||||||
|
<p>{{privacyPolicy[currentLanguage].legalBases}}</p>
|
||||||
|
<p>{{privacyPolicy[currentLanguage].dataDeletion}}</p>
|
||||||
|
<p>{{privacyPolicy[currentLanguage].userRights}}</p>
|
||||||
|
<p>{{privacyPolicy[currentLanguage].objectionRight}}</p>
|
||||||
|
<p>{{privacyPolicy[currentLanguage].complaintRight}}</p>
|
||||||
|
<p>{{privacyPolicy[currentLanguage].changes}}</p>
|
||||||
|
<p>{{privacyPolicy[currentLanguage].lastUpdated}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
49
src/app/Templates/privacy-policy/privacy-policy.component.ts
Normal file
49
src/app/Templates/privacy-policy/privacy-policy.component.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-privacy-policy',
|
||||||
|
templateUrl: './privacy-policy.component.html',
|
||||||
|
styleUrls: ['./privacy-policy.component.css']
|
||||||
|
})
|
||||||
|
export class PrivacyPolicyComponent{
|
||||||
|
|
||||||
|
currentLanguage: string = "english";
|
||||||
|
|
||||||
|
privacyPolicy: { [key: string]:any } = {
|
||||||
|
|
||||||
|
"german": {
|
||||||
|
"introduction": "Diese Datenschutzerklärung klärt die Nutzer über die Art, den Umfang und den Zweck der Erhebung und Verwendung personenbezogener Daten durch [Ihr Unternehmen] auf dieser Website ([Ihre Website-URL]) auf.",
|
||||||
|
"dataController": "Verantwortlicher im Sinne der Datenschutz-Grundverordnung (DSGVO):\n\n[Ihr Unternehmen]\n[Ihre Adresse]\n[Ihre E-Mail-Adresse]\n[Ihre Telefonnummer]",
|
||||||
|
"dataTypes": "- Personenbezogene Daten (z.B. Name, E-Mail-Adresse)\n- Kontaktdaten (z.B. Adresse, Telefonnummer)\n- Nutzungsdaten (z.B. besuchte Webseiten, Interesse an Inhalten, Zugriffszeiten)\n- Meta-/Kommunikationsdaten (z.B. Geräte-Informationen, IP-Adressen)",
|
||||||
|
"dataProcessingPurpose": "- Bereitstellung der Website und ihrer Funktionen\n- Beantwortung von Kontaktanfragen und Kommunikation mit Nutzern\n- Sicherheitsmaßnahmen\n- Reichweitenmessung und Analyse der Nutzeraktivitäten",
|
||||||
|
"legalBases": "Die Verarbeitung personenbezogener Daten erfolgt aufgrund der folgenden Rechtsgrundlagen:\n\n- Einwilligung gemäß Artikel 6 Absatz 1 Buchstabe a DSGVO\n- Erfüllung eines Vertrags oder vorvertraglicher Maßnahmen gemäß Artikel 6 Absatz 1 Buchstabe b DSGVO\n- Erfüllung einer rechtlichen Verpflichtung gemäß Artikel 6 Absatz 1 Buchstabe c DSGVO\n- Schutz lebenswichtiger Interessen der betroffenen Person oder einer anderen natürlichen Person gemäß Artikel 6 Absatz 1 Buchstabe d DSGVO\n- Wahrnehmung einer Aufgabe, die im öffentlichen Interesse liegt oder in Ausübung öffentlicher Gewalt erfolgt, die dem Verantwortlichen übertragen wurde gemäß Artikel 6 Absatz 1 Buchstabe e DSGVO\n- Wahrung der berechtigten Interessen des Verantwortlichen oder eines Dritten gemäß Artikel 6 Absatz 1 Buchstabe f DSGVO",
|
||||||
|
"dataDeletion": "Die personenbezogenen Daten der betroffenen Person werden gelöscht oder gesperrt, sobald der Zweck der Speicherung entfällt. Eine Speicherung kann darüber hinaus dann erfolgen, wenn dies durch den europäischen oder nationalen Gesetzgeber in unionsrechtlichen Verordnungen, Gesetzen oder sonstigen Vorschriften vorgesehen wurde.",
|
||||||
|
"userRights": "- Das Recht auf Auskunft gemäß Artikel 15 DSGVO\n- Das Recht auf Berichtigung gemäß Artikel 16 DSGVO\n- Das Recht auf Löschung gemäß Artikel 17 DSGVO\n- Das Recht auf Einschränkung der Verarbeitung gemäß Artikel 18 DSGVO\n- Das Recht auf Datenübertragbarkeit gemäß Artikel 20 DSGVO\n- Das Recht auf Widerspruch gegen die Verarbeitung gemäß Artikel 21 DSGVO",
|
||||||
|
"objectionRight": "Sofern Ihre personenbezogenen Daten auf Grundlage von berechtigten Interessen gemäß Artikel 6 Absatz 1 Buchstabe f DSGVO verarbeitet werden, haben Sie das Recht, gemäß Artikel 21 DSGVO Widerspruch gegen die Verarbeitung Ihrer personenbezogenen Daten einzulegen, soweit dafür Gründe vorliegen, die sich aus Ihrer besonderen Situation ergeben oder sich der Widerspruch gegen Direktwerbung richtet.",
|
||||||
|
"complaintRight": "Sie haben das Recht, eine Beschwerde bei der zuständigen Aufsichtsbehörde einzulegen, wenn Sie der Ansicht sind, dass die Verarbeitung Ihrer personenbezogenen Daten gegen Datenschutzgesetze verstößt.",
|
||||||
|
"changes": "Wir behalten uns vor, diese Datenschutzerklärung anzupassen, damit sie stets den aktuellen rechtlichen Anforderungen entspricht oder um Änderungen unserer Leistungen in der Datenschutzerklärung umzusetzen, z.B. bei der Einführung neuer Services. Für Ihren erneuten Besuch gilt dann die neue Datenschutzerklärung.",
|
||||||
|
"lastUpdated": "Letzte Aktualisierung: 06/09/2023"
|
||||||
|
},
|
||||||
|
"english": {
|
||||||
|
"introduction": "This privacy policy informs users about the nature, scope, and purpose of the collection and use of personal data by [Your Company] on this website ([Your Website URL]).",
|
||||||
|
"dataController": "Data Controller according to the General Data Protection Regulation (GDPR):\n\n[Your Company]\n[Your Address]\n[Your Email Address]\n[Your Phone Number]",
|
||||||
|
"dataTypes": "- Personal data (e.g., name, email address)\n- Contact details (e.g., address, phone number)\n- Usage data (e.g., visited pages, interest in content, access times)\n- Meta/communication data (e.g., device information, IP addresses)",
|
||||||
|
"dataProcessingPurpose": "- Providing the website and its features\n- Responding to contact requests and communicating with users\n- Security measures\n- Audience measurement and analysis of user activities",
|
||||||
|
"legalBases": "The processing of personal data is based on the following legal bases:\n\n- Consent pursuant to Article 6(1)(a) GDPR\n- Performance of a contract or pre-contractual measures pursuant to Article 6(1)(b) GDPR\n- Compliance with a legal obligation pursuant to Article 6(1)(c) GDPR\n- Protection of vital interests of the data subject or another natural person pursuant to Article 6(1)(d) GDPR\n- Performance of a task carried out in the public interest or in the exercise of official authority vested in the controller pursuant to Article 6(1)(e) GDPR\n- Legitimate interests pursued by the controller or a third party pursuant to Article 6(1)(f) GDPR",
|
||||||
|
"dataDeletion": "Personal data of the data subject will be deleted or blocked as soon as the purpose of storage ceases to apply. Storage may also take place if this has been provided for by the European or national legislator in EU regulations, laws, or other provisions.",
|
||||||
|
"userRights": "- The right to information pursuant to Article 15 GDPR\n- The right to rectification pursuant to Article 16 GDPR\n- The right to erasure ('right to be forgotten') pursuant to Article 17 GDPR\n- The right to restriction of processing pursuant to Article 18 GDPR\n- The right to data portability pursuant to Article 20 GDPR\n- The right to object to processing pursuant to Article 21 GDPR",
|
||||||
|
"objectionRight": "If personal data is processed based on legitimate interests pursuant to Article 6(1)(f) GDPR, data subjects have the right to object to the processing of their personal data for reasons arising from their particular situation or if the objection is directed against direct marketing.",
|
||||||
|
"complaintRight": "You have the right to lodge a complaint with the supervisory authority if you believe that the processing of your personal data violates data protection laws.",
|
||||||
|
"changes": "We reserve the right to amend this privacy policy to ensure that it complies with current legal requirements or to implement changes to our services within the privacy policy, such as the introduction of new services. In such cases, the new privacy policy will apply to your next visit.",
|
||||||
|
"lastUpdated": "Last Updated: 09/06/2023"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
changeLanguage(language: string) {
|
||||||
|
this.currentLanguage = language;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<!-- SingeleChoice -->
|
||||||
|
<div *ngFor="let choice of choices; index as k">
|
||||||
|
<div *ngIf="!choice.selected">
|
||||||
|
<label>
|
||||||
|
<input type="radio" (click)="toggleChoiceSelection(choice)" name="choices">
|
||||||
|
{{ choice.text }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="choice.selected">
|
||||||
|
<label>
|
||||||
|
<input type="radio" (click)="toggleChoiceSelection(choice)" name="choices" checked>
|
||||||
|
{{ choice.text }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { FrageOption } from '../../Model/frage-option';
|
||||||
|
import { QuestionToggleService } from '../../services/question-toggle.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-single-choice-question',
|
||||||
|
templateUrl: './single-choice-questeion.component.html',
|
||||||
|
styleUrls: ['./single-choice-questeion.component.css']
|
||||||
|
})
|
||||||
|
export class SingleChoiceQuestionComponent {
|
||||||
|
@Input() choices: FrageOption[] | undefined;
|
||||||
|
@Output() choiceSelectionToggle = new EventEmitter<FrageOption>()
|
||||||
|
|
||||||
|
constructor(private questService: QuestionToggleService) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleChoiceSelection(choice: FrageOption): void{
|
||||||
|
this.questService.toggleChoiceSelectionSingle(choice);
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/app/app-navigation/app-navigation.component.css
Normal file
84
src/app/app-navigation/app-navigation.component.css
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/* layout.css */
|
||||||
|
|
||||||
|
.side-navigation {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
width: 200px; /* Breite der Seitennavigation */
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* Schatten hinzufügen */
|
||||||
|
padding: 20px 0; /* Vertikaler Abstand oben und unten */
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-navigation a {
|
||||||
|
display: block;
|
||||||
|
padding: 10px 20px; /* Horizontaler Abstand innen */
|
||||||
|
border-radius: 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-navigation a::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: -10px; /* Position des Balkens */
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 5px; /* Breite des Balkens */
|
||||||
|
height: 30px; /* Höhe des Balkens */
|
||||||
|
background-color: transparent; /* Anfangs transparent */
|
||||||
|
transition: background-color 0.3s; /* Übergangseffekt hinzufügen */
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-navigation a.active::after,
|
||||||
|
.side-navigation a:hover::after {
|
||||||
|
background-color: #0ca9f193;
|
||||||
|
/* Farbe ändern, wenn aktiv oder bei Hover */
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-navigation a.active:hover::after {
|
||||||
|
/* Zusätzliche Markierung für aktives Element bei Hover */
|
||||||
|
background-color: #0ca9f193;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zusätzlicher Effekt für geklickte Elemente */
|
||||||
|
.side-navigation a.clicked::after {
|
||||||
|
background-color: #0ca9f193;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fügen Sie den Unterpunkten eine zusätzliche Einrückung hinzu */
|
||||||
|
.side-navigation .sub-navigation {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-navigation .sub-navigation a.active,
|
||||||
|
.side-navigation .sub-navigation a:hover {
|
||||||
|
background-color: #0ca9f193;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zusätzlicher Effekt für geklickte Elemente */
|
||||||
|
.side-navigation .sub-navigation a.clicked {
|
||||||
|
background-color: #0ca9f193;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fügen Sie den Unterpunkten eine zusätzliche Einrückung hinzu */
|
||||||
|
.sub-navigation {
|
||||||
|
margin-left: 20px; /* Hier können Sie den Einrückungsabstand anpassen */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fügen Sie den Unterpunkten eine Hintergrundfarbe hinzu, um sie zu markieren */
|
||||||
|
.sub-navigation a {
|
||||||
|
background-color: #f2f2f2; /* Hintergrundfarbe der Unterpunkte */
|
||||||
|
padding-left: 10px; /* Abstand vom linken Rand */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-navigation a.active,
|
||||||
|
.sub-navigation a:hover {
|
||||||
|
background-color: #0ca9f193; /* Hintergrundfarbe ändern, wenn aktiv oder bei Hover */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zusätzlicher Effekt für geklickte Elemente */
|
||||||
|
.sub-navigation a.clicked {
|
||||||
|
background-color: #0ca9f193; /* Hintergrundfarbe für geklicktes Element */
|
||||||
|
}
|
||||||
|
|
||||||
32
src/app/app-navigation/app-navigation.component.html
Normal file
32
src/app/app-navigation/app-navigation.component.html
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<nav class="side-navigation">
|
||||||
|
<a routerLink="/home" routerLinkActive="active">Home</a>
|
||||||
|
<a (click)="toggleSubMenu('learningMode')" [class.active]="isSubMenuOpen('learningMode')">Learning Mode</a>
|
||||||
|
<div class="sub-navigation" *ngIf="subMenus['learningMode']">
|
||||||
|
<a (click)="toggleSubMenu('fragenliste')" [class.active]="isSubMenuOpen('fragenliste')">Question list</a>
|
||||||
|
<div class="sub-sub-navigation" *ngIf="subMenus['fragenliste']">
|
||||||
|
<a routerLink="/learning-mode/questionlist/LPI101" routerLinkActive="active">LPI101</a>
|
||||||
|
<a routerLink="/learning-mode/questionlist/LPI102" routerLinkActive="active">LPI102</a>
|
||||||
|
</div>
|
||||||
|
<a (click)="toggleSubMenu('einzelfragen')" [class.active]="isSubMenuOpen('einzelfragen')">Single question</a>
|
||||||
|
<div class="sub-sub-navigation" *ngIf="subMenus['einzelfragen']">
|
||||||
|
<a routerLink="/learning-mode/singlequestion/LPI101" routerLinkActive="active">LPI101</a>
|
||||||
|
<a routerLink="/learning-mode/singlequestion/LPI102" routerLinkActive="active">LPI102</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a (click)="toggleSubMenu('checkMode')" [class.active]="isSubMenuOpen('checkMode')">Check Mode</a>
|
||||||
|
<div class="sub-navigation" *ngIf="subMenus['checkMode']">
|
||||||
|
<a routerLink="/check-mode/LPI101" routerLinkActive="active">LPI101</a>
|
||||||
|
<a routerLink="/check-mode/LPI102" routerLinkActive="active">LPI102</a>
|
||||||
|
</div>
|
||||||
|
<a (click)="toggleSubMenu('examMode')" [class.active]="isSubMenuOpen('examMode')">Exam Mode</a>
|
||||||
|
<div class="sub-navigation" *ngIf="subMenus['examMode']">
|
||||||
|
<a routerLink="/exam-mode/LPI101" routerLinkActive="active">LPI101</a>
|
||||||
|
<a routerLink="/exam-mode/LPI102" routerLinkActive="active">LPI102</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
39
src/app/app-navigation/app-navigation.component.ts
Normal file
39
src/app/app-navigation/app-navigation.component.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-app-navigation',
|
||||||
|
templateUrl: './app-navigation.component.html',
|
||||||
|
styleUrls: ['./app-navigation.component.css']
|
||||||
|
})
|
||||||
|
export class AppNavigationComponent {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
subMenus: { [menuName: string]: boolean} = {
|
||||||
|
'learningMode': false,
|
||||||
|
'checkMode': false,
|
||||||
|
'examMode': false,
|
||||||
|
'fragenliste': false,
|
||||||
|
'einzelfragen': false,
|
||||||
|
};
|
||||||
|
|
||||||
|
toggleSubMenu(menuName: string): void {
|
||||||
|
if (this.subMenus[menuName]) {
|
||||||
|
this.router.navigateByUrl('/home'); // Hier wird zur "Home"-Seite navigiert
|
||||||
|
for (const key in this.subMenus) {
|
||||||
|
this.subMenus[key] = false; // Zurücksetzen aller anderen Untermenüs
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.subMenus[menuName] = !this.subMenus[menuName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubMenuOpen(menuName: string): boolean {
|
||||||
|
return this.subMenus[menuName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
26
src/app/app-routing.module.ts
Normal file
26
src/app/app-routing.module.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { LearningCenterComponent } from './learning-center/learning-center.component';
|
||||||
|
import { CheckModeComponent } from './check-mode/check-mode.component';
|
||||||
|
import { ExamModeComponent } from './exam-mode/exam-mode.component';
|
||||||
|
import { StatistikComponent } from './Popup/statistik/statistik.component';
|
||||||
|
import { FilterComponent } from './Templates/filter/filter.component';
|
||||||
|
import { PrivacyPolicyComponent } from './Templates/privacy-policy/privacy-policy.component';
|
||||||
|
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{path: '', redirectTo: 'home', pathMatch:'full'},
|
||||||
|
{path: 'home', component: LearningCenterComponent,},
|
||||||
|
{path: 'learning-mode', loadChildren:() => import('./learning-mode/learning-mode.module').then(m=>m.LearningModeModule)},
|
||||||
|
{path: 'check-mode/:katalog', component: CheckModeComponent},
|
||||||
|
{path: 'exam-mode/:katalog', component: ExamModeComponent},
|
||||||
|
{path: 'statistik', component: StatistikComponent},
|
||||||
|
{path: 'filter', component:FilterComponent},
|
||||||
|
{path: 'privacy', component:PrivacyPolicyComponent},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forRoot(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class AppRoutingModule { }
|
||||||
0
src/app/app.component.css
Normal file
0
src/app/app.component.css
Normal file
19
src/app/app.component.html
Normal file
19
src/app/app.component.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<div class="main-container">
|
||||||
|
|
||||||
|
<nav class="side-navigation">
|
||||||
|
<LL-app-navigation></LL-app-navigation>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="questions-section">
|
||||||
|
<main>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="empty-column">
|
||||||
|
<!-- hier könnte Ihre Werbung stehen -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<LL-cookie-banner></LL-cookie-banner>
|
||||||
|
|
||||||
|
</div>
|
||||||
20
src/app/app.component.ts
Normal file
20
src/app/app.component.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { CookieService } from 'ngx-cookie-service';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-root',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.css']
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private cookieServ: CookieService,
|
||||||
|
) {
|
||||||
|
this.cookieServ.set('cookieName', 'cookieValue')
|
||||||
|
const cookieValue = this.cookieServ.get('cookieName');
|
||||||
|
console.log('Cookie value', cookieValue);
|
||||||
|
}
|
||||||
|
//title = 'LLCE';
|
||||||
|
}
|
||||||
57
src/app/app.module.ts
Normal file
57
src/app/app.module.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { LearningCenterComponent } from './learning-center/learning-center.component';
|
||||||
|
import { CheckModeComponent } from './check-mode/check-mode.component';
|
||||||
|
import { ExamModeComponent } from './exam-mode/exam-mode.component';
|
||||||
|
import { StatistikComponent } from './Popup/statistik/statistik.component';
|
||||||
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { PopUpComponent } from './Popup/pop-up/pop-up.component';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
// import { SingleChoiceQuestionComponent } from './Templates/single-choice-questeion/single-choice-questeion.component';
|
||||||
|
// import { MultipleChoiceQuestionComponent } from './Templates/multiple-choice-questeion/multiple-choice-questeion.component';
|
||||||
|
// import { InputChoiceQuesteionComponent } from './Templates/input-choice-questeion/input-choice-questeion.component';
|
||||||
|
import { AppNavigationComponent } from './app-navigation/app-navigation.component';
|
||||||
|
import { FilterComponent } from './Templates/filter/filter.component';
|
||||||
|
import { CookieBannerComponent } from './cookie-banner/cookie-banner.component';
|
||||||
|
import { PrivacyPolicyComponent } from './Templates/privacy-policy/privacy-policy.component';
|
||||||
|
import { ErrorMessageComponent } from './Popup/error-message/error-message.component';
|
||||||
|
import { SharedModule } from './shared/shared.module';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent,
|
||||||
|
// LearningCenterComponent,
|
||||||
|
CheckModeComponent,
|
||||||
|
ExamModeComponent,
|
||||||
|
StatistikComponent,
|
||||||
|
PopUpComponent,
|
||||||
|
// SingleChoiceQuestionComponent,
|
||||||
|
// MultipleChoiceQuestionComponent,
|
||||||
|
// InputChoiceQuesteionComponent,
|
||||||
|
AppNavigationComponent,
|
||||||
|
FilterComponent,
|
||||||
|
CookieBannerComponent,
|
||||||
|
PrivacyPolicyComponent,
|
||||||
|
ErrorMessageComponent,
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
AppRoutingModule,
|
||||||
|
HttpClientModule,
|
||||||
|
FormsModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
MatDialogModule,
|
||||||
|
SharedModule,
|
||||||
|
],
|
||||||
|
providers: [],
|
||||||
|
bootstrap: [AppComponent]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
||||||
0
src/app/check-mode/check-mode.component.css
Normal file
0
src/app/check-mode/check-mode.component.css
Normal file
43
src/app/check-mode/check-mode.component.html
Normal file
43
src/app/check-mode/check-mode.component.html
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<!-- Begin -->
|
||||||
|
<div class="questions-section">
|
||||||
|
<div class="question-container">
|
||||||
|
<div class="question-block" *ngIf="getQuestionNav().getCurrentQuestion()">
|
||||||
|
|
||||||
|
<h3>Question {{ getTrueFalse().getCurrentQuestionIndex() + 1 }}</h3>
|
||||||
|
|
||||||
|
<div class="question-text">{{ getQuestionNav().getCurrentQuestion().questionText }}</div>
|
||||||
|
|
||||||
|
<div class="choice-text">
|
||||||
|
<LL-single-choice-question
|
||||||
|
*ngIf="getQuestionNav().getCurrentQuestion().questionType === 'single'"
|
||||||
|
[choices]="getQuestionNav().getCurrentQuestion().choices"
|
||||||
|
(choiceSelectionToggle)="toggleChoiceSelectionSingle($event)"
|
||||||
|
></LL-single-choice-question>
|
||||||
|
|
||||||
|
<LL-multiple-choice-question
|
||||||
|
*ngIf="getQuestionNav().getCurrentQuestion().questionType === 'multiple'"
|
||||||
|
[choices]="getQuestionNav().getCurrentQuestion().choices"
|
||||||
|
(choiceSelectionToggle)="toggleChoiceSelectionMulti($event)"
|
||||||
|
></LL-multiple-choice-question>
|
||||||
|
|
||||||
|
<LL-input-choice-question
|
||||||
|
*ngIf="getQuestionNav().getCurrentQuestion().questionType === 'input'"
|
||||||
|
[userAnswer]="getTrueFalse().getCurrentAnswers().userAnswer"
|
||||||
|
(userAnswerChange)="toggleInputAnswer($event)"
|
||||||
|
></LL-input-choice-question>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Question Navigation -->
|
||||||
|
<div class="button-container">
|
||||||
|
<button *ngIf="!getQuestionNav().thisIsLastQuestion()" (click)="getQuestionNav().movePreviusQuestion()">Back</button>
|
||||||
|
<button *ngIf="!getQuestionNav().thisIsLastQuestion()" (click)="getQuestionNav().moveNextQuestionCheckMode()">Forward</button>
|
||||||
|
<button *ngIf="getQuestionNav().thisIsLastQuestion()" (click)="getQuestionNav().endExam()">Finish</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button-container">
|
||||||
|
<button *ngIf="!getQuestionNav().thisIsLastQuestion()" (click)="getQuestionNav().skipQuestion()">Skip</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
59
src/app/check-mode/check-mode.component.ts
Normal file
59
src/app/check-mode/check-mode.component.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { TrueOrFalesService } from '../services/true-or-false.service';
|
||||||
|
import { QuestionNavigationService } from '../services/question-navigation.service';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { QuestionToggleService } from '../services/question-toggle.service';
|
||||||
|
import { StatistikService } from '../services/statistik.service';
|
||||||
|
import { FrageOption } from '../Model/frage-option';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-check-mode',
|
||||||
|
templateUrl: './check-mode.component.html',
|
||||||
|
styleUrls: ['./check-mode.component.css'],
|
||||||
|
})
|
||||||
|
export class CheckModeComponent implements OnInit{
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private trueFalse: TrueOrFalesService,
|
||||||
|
private questNav: QuestionNavigationService,
|
||||||
|
private questService: QuestionToggleService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private statServ: StatistikService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.route.params.subscribe((params) => {
|
||||||
|
const katalog = params['katalog'];
|
||||||
|
if (katalog === 'LPI101') {
|
||||||
|
this.trueFalse.loadKatalogAndQuestions().subscribe(() => {
|
||||||
|
});
|
||||||
|
} else if (katalog === 'LPI102') {
|
||||||
|
this.trueFalse.loadKatalog2AndQuestions().subscribe(() => {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.statServ.resetStatistics();
|
||||||
|
}
|
||||||
|
|
||||||
|
getQuestionNav(): QuestionNavigationService {
|
||||||
|
return this.questNav;
|
||||||
|
}
|
||||||
|
getTrueFalse(): TrueOrFalesService {
|
||||||
|
return this.trueFalse;
|
||||||
|
}
|
||||||
|
getQuestionServ(): QuestionToggleService{
|
||||||
|
return this.questService;
|
||||||
|
}
|
||||||
|
toggleChoiceSelectionSingle(choice: FrageOption): void{
|
||||||
|
this.questService.toggleChoiceSelectionSingle(choice);
|
||||||
|
}
|
||||||
|
toggleChoiceSelectionMulti(choice: FrageOption): void{
|
||||||
|
this.questService.toggleChoiceSelectionMulti(choice);
|
||||||
|
}
|
||||||
|
toggleInputAnswer(answer: string | undefined): void{
|
||||||
|
this.questService.toggleInputAnswer(answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
22
src/app/cookie-banner/cookie-banner.component.css
Normal file
22
src/app/cookie-banner/cookie-banner.component.css
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
.cookie-banner {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0px -2px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cookie-banner button {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cookie-banner button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
10
src/app/cookie-banner/cookie-banner.component.html
Normal file
10
src/app/cookie-banner/cookie-banner.component.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
<div class="cookie-banner" *ngIf="!hasConsent()">
|
||||||
|
<span>This website uses cookies to enhance your experience. Please accept the use of cookies.</span>
|
||||||
|
<div>
|
||||||
|
<a href="/privacy">Privacy Policy</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button (click)="giveConsent()">Accept</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
45
src/app/cookie-banner/cookie-banner.component.ts
Normal file
45
src/app/cookie-banner/cookie-banner.component.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { CookieService } from 'ngx-cookie-service';
|
||||||
|
import { DataInput } from '../services/data-input';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-cookie-banner',
|
||||||
|
templateUrl: './cookie-banner.component.html',
|
||||||
|
styleUrls: ['./cookie-banner.component.css']
|
||||||
|
})
|
||||||
|
export class CookieBannerComponent {
|
||||||
|
privacyPolice: any;
|
||||||
|
currentLanguage: string = 'english';
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private cookieServ: CookieService,
|
||||||
|
){}
|
||||||
|
|
||||||
|
giveConsent(){
|
||||||
|
this.cookieServ.set('cookieConsent', 'true');
|
||||||
|
this.setNecessaryCookies();
|
||||||
|
}
|
||||||
|
|
||||||
|
setNecessaryCookies(){
|
||||||
|
this.cookieServ.set('exampleCookie', 'exampleValue');
|
||||||
|
this.cookieServ.set('authCookie', 'authValue');
|
||||||
|
}
|
||||||
|
|
||||||
|
revokeConsent(){
|
||||||
|
this.cookieServ.delete('cookieConsent');
|
||||||
|
this.cookieServ.delete('exampleCookie');
|
||||||
|
this.cookieServ.delete('authCookie');
|
||||||
|
}
|
||||||
|
|
||||||
|
hasConsent(): boolean{
|
||||||
|
return this.cookieServ.get('cookieConsent') === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
changeLanguage(language: string) {
|
||||||
|
this.currentLanguage = language;
|
||||||
|
this.cookieServ.set('selectedLanguage', language);
|
||||||
|
// Füge hier den Code zum Laden der Datenschutzrichtlinieninhalte in der ausgewählten Sprache hinzu
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
60
src/app/exam-mode/exam-mode.component.css
Normal file
60
src/app/exam-mode/exam-mode.component.css
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/* CSS-Stile für das Schiebeelement (Toggle) */
|
||||||
|
.toggle-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-label {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-label input[type="checkbox"] {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
transition: 0.4s;
|
||||||
|
border-radius: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: white;
|
||||||
|
transition: 0.4s;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:checked + .slider {
|
||||||
|
background-color: #2196F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:checked + .slider:before {
|
||||||
|
transform: translateX(26px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider.round {
|
||||||
|
border-radius: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider.round:before {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
52
src/app/exam-mode/exam-mode.component.html
Normal file
52
src/app/exam-mode/exam-mode.component.html
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<!-- Begin -->
|
||||||
|
<div class="questions-section">
|
||||||
|
|
||||||
|
<div class="toggle-container">
|
||||||
|
<label class="toggle-label">
|
||||||
|
<input type="checkbox" [(ngModel)]="examModeEnabled" (change)="toggleExamMode()">
|
||||||
|
<span class="slider round"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="question-container">
|
||||||
|
<div class="question-block" *ngIf="getQuestionNav().getCurrentQuestion()">
|
||||||
|
|
||||||
|
<h3>Question {{ getTrueFalse().getCurrentQuestionIndex() + 1 }}</h3>
|
||||||
|
|
||||||
|
<div class="question-text">{{ getQuestionNav().getCurrentQuestion().questionText }}</div>
|
||||||
|
|
||||||
|
<div class="choice-text">
|
||||||
|
<LL-single-choice-question
|
||||||
|
*ngIf="getQuestionNav().getCurrentQuestion().questionType === 'single'"
|
||||||
|
[choices]="getQuestionNav().getCurrentQuestion().choices"
|
||||||
|
(choiceSelectionToggle)="toggleChoiceSelectionSingle($event)"
|
||||||
|
></LL-single-choice-question>
|
||||||
|
|
||||||
|
<LL-multiple-choice-question
|
||||||
|
*ngIf="getQuestionNav().getCurrentQuestion().questionType === 'multiple'"
|
||||||
|
[choices]="getQuestionNav().getCurrentQuestion().choices"
|
||||||
|
(choiceSelectionToggle)="toggleChoiceSelectionMulti($event)"
|
||||||
|
></LL-multiple-choice-question>
|
||||||
|
|
||||||
|
<LL-input-choice-question
|
||||||
|
*ngIf="getQuestionNav().getCurrentQuestion().questionType === 'input'"
|
||||||
|
[userAnswer]="getTrueFalse().getCurrentAnswers().userAnswer"
|
||||||
|
(userAnswerChange)="toggleInputAnswer($event)"
|
||||||
|
></LL-input-choice-question>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Question Navigation -->
|
||||||
|
|
||||||
|
<div class="button-container">
|
||||||
|
<button *ngIf="!getQuestionNav().thisIsLastQuestion()" (click)="getQuestionNav().movePreviusQuestion()">Back</button>
|
||||||
|
<button *ngIf="!getQuestionNav().thisIsLastQuestion()" (click)="getQuestionNav().moveNextQuestionExamMode()">Forward</button>
|
||||||
|
<button *ngIf="getQuestionNav().thisIsLastQuestion()" (click)="getQuestionNav().endExam()">Finish</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button-container">
|
||||||
|
<button *ngIf="!getQuestionNav().thisIsLastQuestion()" (click)="getQuestionNav().endExam()">Quit Exam</button>
|
||||||
|
</div>
|
||||||
104
src/app/exam-mode/exam-mode.component.ts
Normal file
104
src/app/exam-mode/exam-mode.component.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { TrueOrFalesService } from '../services/true-or-false.service';
|
||||||
|
import { QuestionNavigationService } from '../services/question-navigation.service';
|
||||||
|
import { FrageOption } from '../Model/frage-option';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { QuestionToggleService } from '../services/question-toggle.service';
|
||||||
|
import { StatistikService } from '../services/statistik.service';
|
||||||
|
import { Question } from '../Model/question';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-exam-mode',
|
||||||
|
templateUrl: './exam-mode.component.html',
|
||||||
|
styleUrls: ['./exam-mode.component.css'],
|
||||||
|
})
|
||||||
|
export class ExamModeComponent implements OnInit {
|
||||||
|
|
||||||
|
examModeEnabled: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private trueFalse: TrueOrFalesService,
|
||||||
|
private questNav: QuestionNavigationService,
|
||||||
|
private toggle: QuestionToggleService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private statServ: StatistikService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.route.params.subscribe((params) => {
|
||||||
|
const katalog = params['katalog'];
|
||||||
|
if (katalog === 'LPI101') {
|
||||||
|
this.trueFalse.loadKatalogAndQuestions().subscribe(() => {});
|
||||||
|
} else if (katalog === 'LPI102') {
|
||||||
|
this.trueFalse.loadKatalog2AndQuestions().subscribe(() => {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.statServ.resetStatistics();
|
||||||
|
}
|
||||||
|
|
||||||
|
getQuestionNav(): QuestionNavigationService {
|
||||||
|
return this.questNav;
|
||||||
|
}
|
||||||
|
getTrueFalse(): TrueOrFalesService {
|
||||||
|
return this.trueFalse;
|
||||||
|
}
|
||||||
|
getToggle(): QuestionToggleService {
|
||||||
|
return this.toggle;
|
||||||
|
}
|
||||||
|
toggleChoiceSelectionSingle(choice: FrageOption): void {
|
||||||
|
this.toggle.toggleChoiceSelectionSingle(choice);
|
||||||
|
}
|
||||||
|
toggleChoiceSelectionMulti(choice: FrageOption): void {
|
||||||
|
this.toggle.toggleChoiceSelectionMulti(choice);
|
||||||
|
}
|
||||||
|
toggleInputAnswer(answer: string | undefined): void {
|
||||||
|
this.toggle.toggleInputAnswer(answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleExamMode(): void {
|
||||||
|
this.examModeEnabled = !this.examModeEnabled;
|
||||||
|
if (this.examModeEnabled) {
|
||||||
|
this.applyExamModeToCurrentKatalog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyExamModeToCurrentKatalog(): void {
|
||||||
|
const katalog = this.route.snapshot.params['katalog']; // Holt sich den aktuellen Katalog aus den Route-Parametern.
|
||||||
|
|
||||||
|
if (katalog === 'LPI101') {
|
||||||
|
this.applyExamModeToKatalog1();
|
||||||
|
} else if (katalog === 'LPI102') {
|
||||||
|
this.applyExamModeToKatalog2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyExamModeToKatalog1(): void {
|
||||||
|
const questionsArray = this.trueFalse.getKatalog1()
|
||||||
|
this.processQuestions(questionsArray)
|
||||||
|
}
|
||||||
|
|
||||||
|
applyExamModeToKatalog2(): void {
|
||||||
|
const questionsArray = this.trueFalse.getKatalog2()
|
||||||
|
this.processQuestions(questionsArray)
|
||||||
|
}
|
||||||
|
|
||||||
|
processQuestions(questionsArray: Question[]): void {
|
||||||
|
for (let i = 0; i < questionsArray.length; i++) {
|
||||||
|
const currentQuestion = questionsArray[i];
|
||||||
|
if (currentQuestion.questionType === 'single' || currentQuestion.questionType === 'multiple') {
|
||||||
|
// Mischen der Antwortmöglichkeiten
|
||||||
|
currentQuestion.choices = this.shuffleArray(currentQuestion.choices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffleArray(array: any[]): any[] {
|
||||||
|
const shuffledArray = [...array];
|
||||||
|
for (let i = shuffledArray.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
|
||||||
|
}
|
||||||
|
return shuffledArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
50
src/app/learning-center/learning-center.component.css
Normal file
50
src/app/learning-center/learning-center.component.css
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
.navigation-links {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px; /* Abstand zwischen den Links */
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-navigation {
|
||||||
|
margin-top: 100px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center; /* Zentrieren der Navigation */
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-links a {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc; /* Rahmen um die Links */
|
||||||
|
border-radius: 5px; /* Abrundung des Rahmens */
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-links a.active {
|
||||||
|
background-color: #f0f0f0; /* Hintergrundfarbe für aktiven Link */
|
||||||
|
}
|
||||||
|
|
||||||
|
.fixed-navigation {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff; /* Hintergrundfarbe der Navigation */
|
||||||
|
z-index: 1000; /* Stelle sicher, dass die Navigation über anderen Inhalten liegt */
|
||||||
|
border-bottom: 1px solid #ccc; /* Optional: Trennlinie unter der Navigation */
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoTextBox{
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-infoTextBox{
|
||||||
|
margin-top: 100px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centeredHeadline{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
12
src/app/learning-center/learning-center.component.html
Normal file
12
src/app/learning-center/learning-center.component.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<h1 class="centeredHeadline">Linux Learch Check Exam LLCE</h1>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="centered-infoTextBox">
|
||||||
|
<div class="infoTextBox">
|
||||||
|
<h4>Welcome to the Linux LPIC1 101 and 102 Certification Exam Simulator!</h4>
|
||||||
|
<p>Are you ready to embark on your journey towards becoming a certified
|
||||||
|
Linux professional? Look no further – our comprehensive platform is
|
||||||
|
your ultimate resource for mastering the LPIC1 101 and 102 certification exams.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
13
src/app/learning-center/learning-center.component.ts
Normal file
13
src/app/learning-center/learning-center.component.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { NavigationEnd, Router } from '@angular/router';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-learning-center',
|
||||||
|
templateUrl: './learning-center.component.html',
|
||||||
|
styleUrls: ['./learning-center.component.css']
|
||||||
|
})
|
||||||
|
export class LearningCenterComponent {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<!--Einzelfragen -->
|
||||||
|
<div class="questions-section">
|
||||||
|
<div class="question-container">
|
||||||
|
<div class="question-block" *ngIf="getQuestionNav().getCurrentQuestion()">
|
||||||
|
|
||||||
|
<h3>Question {{getTrueFalse().getCurrentQuestionIndex() + 1 }}</h3>
|
||||||
|
|
||||||
|
<div class="question-text">{{getQuestionNav().getCurrentQuestion().questionText}}</div>
|
||||||
|
|
||||||
|
<div class="choice-text">
|
||||||
|
<ng-container *ngIf="getQuestionNav().getCurrentQuestion().questionType === 'single'">
|
||||||
|
<LL-single-choice-question [choices]="getQuestionNav().getCurrentQuestion().choices"></LL-single-choice-question>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="getQuestionNav().getCurrentQuestion().questionType === 'multiple'">
|
||||||
|
<LL-multiple-choice-question [choices]="getQuestionNav().getCurrentQuestion().choices"></LL-multiple-choice-question>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="getQuestionNav().getCurrentQuestion().questionType === 'input'">
|
||||||
|
<LL-input-choice-question
|
||||||
|
[userAnswer]="getTrueFalse().getCurrentAnswers().userAnswer"></LL-input-choice-question>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<button class="show-answer-button" (click)="showAnswer = !showAnswer">Answer</button>
|
||||||
|
<div class="answer" *ngIf="showAnswer && getQuestionNav().getCurrentQuestion().answer">
|
||||||
|
Answer: {{getQuestionNav().getCurrentQuestion().answer}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="button-container">
|
||||||
|
<button (click)="getQuestionNav().movePreviusQuestion()">Back</button>
|
||||||
|
<button (click)="getQuestionNav().moveNextQuestionWithOutCheck(); showAnswer = false;">Forward</button>
|
||||||
|
</div>
|
||||||
47
src/app/learning-mode/einzelfragen/einzelfragen.component.ts
Normal file
47
src/app/learning-mode/einzelfragen/einzelfragen.component.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
import { TrueOrFalesService } from 'src/app/services/true-or-false.service';
|
||||||
|
import { QuestionNavigationService } from 'src/app/services/question-navigation.service';
|
||||||
|
import { Question } from 'src/app/Model/question';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { QuestionToggleService } from 'src/app/services/question-toggle.service';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-einzelfragen',
|
||||||
|
templateUrl: './einzelfragen.component.html',
|
||||||
|
styleUrls: ['./einzelfragen.component.css'],
|
||||||
|
})
|
||||||
|
export class EinzelfragenComponent implements OnInit {
|
||||||
|
filteredQuestions: Question[] = [];
|
||||||
|
showAnswer: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private trueFalse: TrueOrFalesService,
|
||||||
|
private questNav: QuestionNavigationService,
|
||||||
|
private questService: QuestionToggleService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.route.params.subscribe((params) => {
|
||||||
|
const katalog = params['katalog'];
|
||||||
|
if (katalog === 'LPI101') {
|
||||||
|
this.trueFalse.loadKatalogAndQuestions().subscribe(() => {});
|
||||||
|
} else if (katalog === 'LPI102') {
|
||||||
|
this.trueFalse.loadKatalog2AndQuestions().subscribe(() => {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getQuestionNav(): QuestionNavigationService {
|
||||||
|
return this.questNav;
|
||||||
|
}
|
||||||
|
getTrueFalse(): TrueOrFalesService {
|
||||||
|
return this.trueFalse;
|
||||||
|
}
|
||||||
|
getQuestService(): QuestionToggleService{
|
||||||
|
return this.questService;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.answer {
|
||||||
|
color: green; /* Beispiel: Ändere die Farbe auf Grün */
|
||||||
|
font-weight: bold; /* Beispiel: Ändere die Schriftart auf fett */
|
||||||
|
}
|
||||||
31
src/app/learning-mode/fragenliste/fragenliste.component.html
Normal file
31
src/app/learning-mode/fragenliste/fragenliste.component.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<div class="questions-section">
|
||||||
|
<div class="question-type-buttons">
|
||||||
|
<button class="show-answer-button" mat-button (click)="navigateToFilterPage()">Filter Questions</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="question-container">
|
||||||
|
<div class="question-block" *ngFor="let question of getKatalog(); let i = index">
|
||||||
|
<h3>Question {{i+1}}</h3>
|
||||||
|
<div class="question-text">{{question.questionText}}</div>
|
||||||
|
|
||||||
|
<div class="choice-text">
|
||||||
|
<ng-container *ngIf="question.questionType === 'single'">
|
||||||
|
<LL-single-choice-question [choices]="question.choices"></LL-single-choice-question>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="question.questionType === 'multiple'">
|
||||||
|
<LL-multiple-choice-question [choices]="question.choices"></LL-multiple-choice-question>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="question.questionType === 'input'">
|
||||||
|
<LL-input-choice-question
|
||||||
|
[userAnswer]="getTrueFlase().getCurrentAnswers().userAnswer"></LL-input-choice-question>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="show-answer-button" (click)="getQuestService().toggleAnswerVisibility()">Answer</button>
|
||||||
|
<div class="answer" *ngIf="getQuestService().getShowAnswer()">Right Answer: {{question.answer}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
63
src/app/learning-mode/fragenliste/fragenliste.component.ts
Normal file
63
src/app/learning-mode/fragenliste/fragenliste.component.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
import { TrueOrFalesService } from 'src/app/services/true-or-false.service';
|
||||||
|
import { QuestionNavigationService } from 'src/app/services/question-navigation.service';
|
||||||
|
import { Question } from 'src/app/Model/question';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { QuestionToggleService } from 'src/app/services/question-toggle.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-fragenliste',
|
||||||
|
templateUrl: './fragenliste.component.html',
|
||||||
|
styleUrls: ['./fragenliste.component.css'],
|
||||||
|
})
|
||||||
|
export class FragenlisteComponent implements OnInit {
|
||||||
|
constructor(
|
||||||
|
private trueFalse: TrueOrFalesService,
|
||||||
|
private questService: QuestionToggleService,
|
||||||
|
private questNav: QuestionNavigationService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
selectedQuestionIndex: number = -1;
|
||||||
|
showAnswerIndex: number | null = null;
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.route.params.subscribe((params) => {
|
||||||
|
const katalog = params['katalog'];
|
||||||
|
if (katalog === 'LPI101') {
|
||||||
|
this.trueFalse.loadKatalogAndQuestions().subscribe(() => {});
|
||||||
|
} else if (katalog === 'LPI102') {
|
||||||
|
this.trueFalse.loadKatalog2AndQuestions().subscribe(() => {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showAnswer(index: number):void{
|
||||||
|
this.showAnswerIndex = index;
|
||||||
|
}
|
||||||
|
getTrueFlase(): TrueOrFalesService {
|
||||||
|
return this.trueFalse;
|
||||||
|
}
|
||||||
|
getQuestNav(): QuestionNavigationService {
|
||||||
|
return this.questNav;
|
||||||
|
}
|
||||||
|
getKatalog(): Question[] {
|
||||||
|
return this.trueFalse.getKatalog1();
|
||||||
|
}
|
||||||
|
getQuestService(): QuestionToggleService{
|
||||||
|
return this.questService;
|
||||||
|
}
|
||||||
|
selectedQuestion(index:number):void{
|
||||||
|
this.selectedQuestionIndex = index;
|
||||||
|
}
|
||||||
|
navigateToFilterPage(): void {
|
||||||
|
this.router.navigate(['/filter']); // Hier den Pfad zur Filterseite einfügen
|
||||||
|
}
|
||||||
|
navigateToRoot():void{
|
||||||
|
this.router.navigate(['/'])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
17
src/app/learning-mode/learning-mode-routing.module.ts
Normal file
17
src/app/learning-mode/learning-mode-routing.module.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { FragenlisteComponent } from './fragenliste/fragenliste.component';
|
||||||
|
import { EinzelfragenComponent } from './einzelfragen/einzelfragen.component';
|
||||||
|
import { LearningModeComponent } from './learning-mode.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{ path: '', component: LearningModeComponent},
|
||||||
|
{ path: 'questionlist/:katalog', component: FragenlisteComponent},
|
||||||
|
{ path: 'singlequestion/:katalog', component: EinzelfragenComponent},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class LearningModeRoutingModule { }
|
||||||
53
src/app/learning-mode/learning-mode.component.css
Normal file
53
src/app/learning-mode/learning-mode.component.css
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
.navigation-links {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px; /* Abstand zwischen den Links */
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-navigation {
|
||||||
|
margin-top: 100px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center; /* Zentrieren der Navigation */
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-links a {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc; /* Rahmen um die Links */
|
||||||
|
border-radius: 5px; /* Abrundung des Rahmens */
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-links a.active {
|
||||||
|
background-color: #0ca9f193; /* Hintergrundfarbe für aktiven Link */
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoTextBox{
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-infoTextBox{
|
||||||
|
margin-top: 100px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subNavigation-links {
|
||||||
|
display: flex;
|
||||||
|
gap: 50px; /* Abstand zwischen den Links */
|
||||||
|
}
|
||||||
|
|
||||||
|
.subNavigation-links a {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc; /* Rahmen um die Links */
|
||||||
|
border-radius: 5px; /* Abrundung des Rahmens */
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subNavigation-links a.active {
|
||||||
|
background-color: #0ca9f193; /* Hintergrundfarbe für aktiven Link */
|
||||||
|
}
|
||||||
1
src/app/learning-mode/learning-mode.component.html
Normal file
1
src/app/learning-mode/learning-mode.component.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
10
src/app/learning-mode/learning-mode.component.ts
Normal file
10
src/app/learning-mode/learning-mode.component.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'LL-learning-mode',
|
||||||
|
templateUrl: './learning-mode.component.html',
|
||||||
|
styleUrls: ['./learning-mode.component.css']
|
||||||
|
})
|
||||||
|
export class LearningModeComponent {
|
||||||
|
|
||||||
|
}
|
||||||
25
src/app/learning-mode/learning-mode.module.ts
Normal file
25
src/app/learning-mode/learning-mode.module.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { LearningModeRoutingModule } from './learning-mode-routing.module';
|
||||||
|
import { LearningModeComponent } from './learning-mode.component';
|
||||||
|
import { FragenlisteComponent } from './fragenliste/fragenliste.component';
|
||||||
|
import { EinzelfragenComponent } from './einzelfragen/einzelfragen.component';
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
LearningModeComponent,
|
||||||
|
FragenlisteComponent,
|
||||||
|
EinzelfragenComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
LearningModeRoutingModule,
|
||||||
|
SharedModule,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class LearningModeModule { }
|
||||||
40
src/app/services/data-input.ts
Normal file
40
src/app/services/data-input.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Observable, combineLatest, map } from 'rxjs';
|
||||||
|
import { Question } from '../Model/question';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class DataInput {
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
private katalog1Url = 'assets/LPI-2019-1-101d-QA.json';
|
||||||
|
private katalog2Url = 'assets/LPI-2019-1-102d-QA.json';
|
||||||
|
|
||||||
|
getQuestion1(): Observable<Question[]> {
|
||||||
|
return this.http.get<Question[]>(this.katalog1Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
getQuestion2(): Observable<Question[]> {
|
||||||
|
return this.http.get<Question[]>(this.katalog2Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
getKatalog1Url(): string {
|
||||||
|
return this.katalog1Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
getKaltalog2Url(): string {
|
||||||
|
return this.katalog2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllQuestions(): Observable<Question[]>{
|
||||||
|
const question1 = this.getQuestion1();
|
||||||
|
const question2 = this.getQuestion2();
|
||||||
|
|
||||||
|
return combineLatest([question1, question2]).pipe(
|
||||||
|
map(([question1,question2]) => [...question1, ...question2])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/app/services/filter.service.ts
Normal file
62
src/app/services/filter.service.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Question } from '../Model/question';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import { DataInput } from './data-input';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class FilterService {
|
||||||
|
selectedType: string = '';
|
||||||
|
filteredQuestionsLPI101: Question[] = [];
|
||||||
|
filteredQuestionsLPI102: Question[] = [];
|
||||||
|
questions: Question[] = [];
|
||||||
|
qustionTypesToShow: string[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private mainService: DataInput,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private selectedTypeSource = new BehaviorSubject<string>(''); // Initialwert
|
||||||
|
selectedType$ = this.selectedTypeSource.asObservable();
|
||||||
|
|
||||||
|
setQuestionTypesToShow(questionTypes: string[]): void {
|
||||||
|
this.qustionTypesToShow = questionTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedType(selectedType: string): void {
|
||||||
|
this.selectedTypeSource.next(selectedType);
|
||||||
|
this.updateFilteredQuestions(selectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
setQuestions(questions: Question[]): void {
|
||||||
|
this.questions = questions;
|
||||||
|
}
|
||||||
|
|
||||||
|
filterByQuestionsTypes(questions: Question[], questionType: string): Question[] {
|
||||||
|
console.log(questions,'Questions');
|
||||||
|
console.log(this.questions,'Katalog');
|
||||||
|
|
||||||
|
const filtered = questions.filter(question => question.questionType == questionType)
|
||||||
|
|
||||||
|
console.log(questionType,'Filtered');
|
||||||
|
console.log(filtered,'Filtered');
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFilteredQuestions(selectedType: string): void {
|
||||||
|
this.mainService.getQuestion1().subscribe(questionsLPI101 => {
|
||||||
|
this.filteredQuestionsLPI101 = this.filterByQuestionsTypes(questionsLPI101, selectedType);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mainService.getQuestion2().subscribe(questionsLPI102 => {
|
||||||
|
const filteredQuestionsLPI102 = this.filterByQuestionsTypes(questionsLPI102, selectedType);
|
||||||
|
this.filteredQuestionsLPI102 = filteredQuestionsLPI102;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
54
src/app/services/popup.service.ts
Normal file
54
src/app/services/popup.service.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { PopUpComponent } from '../Popup/pop-up/pop-up.component';
|
||||||
|
import { StatistikComponent } from '../Popup/statistik/statistik.component';
|
||||||
|
import { ErrorMessageComponent } from '../Popup/error-message/error-message.component';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class PopupService {
|
||||||
|
constructor(
|
||||||
|
private dialog: MatDialog,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
openPopUp(message: string): void {
|
||||||
|
this.dialog.open(PopUpComponent, {
|
||||||
|
data: { message: message },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
errorPopup(message: string): void {
|
||||||
|
this.dialog.open(ErrorMessageComponent , {
|
||||||
|
data: { message: message },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openStatisticPopup(message: string): void {
|
||||||
|
this.dialog.open(StatistikComponent, {
|
||||||
|
data: { message: message },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dontMoveWitNowAnswer(): void {
|
||||||
|
this.openPopUp('Please provide an answer before proceeding');
|
||||||
|
}
|
||||||
|
|
||||||
|
wrongAnswerMessage(): void {
|
||||||
|
this.openPopUp( //ändern
|
||||||
|
"There are two possibilities:\n\nA) You were not paying attention\nB) You were not honest\nIf you really don't know, skip the question."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
backToLearningCenterExam(): void {
|
||||||
|
this.errorPopup(
|
||||||
|
'You have already answered more than 20% of the questions incorrectly. The exam will be terminated.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
backToLearningCenterCheck(): void {
|
||||||
|
this.errorPopup(
|
||||||
|
'You have already answered 7 of the questions incorrectly. The check will be terminated.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
292
src/app/services/question-navigation.service.ts
Normal file
292
src/app/services/question-navigation.service.ts
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { TrueOrFalesService } from './true-or-false.service';
|
||||||
|
import { Question } from '../Model/question';
|
||||||
|
import { AnswerData } from '../Model/answer-data';
|
||||||
|
import { PopupService } from './popup.service';
|
||||||
|
import { StatistikService } from './statistik.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class QuestionNavigationService {
|
||||||
|
private currentQuestionIndex: number = 0;
|
||||||
|
private currentQuestion!: Question;
|
||||||
|
private isFirstQuestion: boolean = true;
|
||||||
|
private isLastQuestion: boolean = false;
|
||||||
|
private answeredQuestions: boolean[] = [];
|
||||||
|
private totalQuestions: number = 0;
|
||||||
|
private answerData: AnswerData[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private trueOrFalseService: TrueOrFalesService,
|
||||||
|
private popupServ: PopupService,
|
||||||
|
private statistServ: StatistikService,
|
||||||
|
private router: Router
|
||||||
|
) {}
|
||||||
|
|
||||||
|
initializeNavigation(totalQuestions: number): void {
|
||||||
|
this.totalQuestions = totalQuestions;
|
||||||
|
this.currentQuestionIndex = 0;
|
||||||
|
this.isFirstQuestion = true;
|
||||||
|
this.isLastQuestion = totalQuestions === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private recordAnswerData(userAnswer: string, isAnswerCorrect: boolean): void {
|
||||||
|
this.answerData.push({
|
||||||
|
question: this.trueOrFalseService.getCurrentQuestion(),
|
||||||
|
userAnswer: userAnswer,
|
||||||
|
isCorrect: isAnswerCorrect,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
moveNextQuestionCheckMode(): void {
|
||||||
|
const currentAnswers = this.trueOrFalseService.getCurrentAnswers();
|
||||||
|
const userAnswer = currentAnswers.userAnswer;
|
||||||
|
if (!this.isLastQuestion) {
|
||||||
|
//Überprüfen ob die Antwort korrekt ist
|
||||||
|
if (this.shouldSkipQuestion(userAnswer)) {
|
||||||
|
if (this.trueOrFalseService.getSkipped()) {
|
||||||
|
this.statistServ.incrementCounterOfSkip();
|
||||||
|
this.moveNextQuestionWithOutCheck();
|
||||||
|
this.trueOrFalseService.setSkipped(false);
|
||||||
|
} else {
|
||||||
|
this.popupServ.dontMoveWitNowAnswer();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const isAnswerCorrect = this.trueOrFalseService.checkAnswer(userAnswer);
|
||||||
|
this.evalueteAnswer(userAnswer, isAnswerCorrect);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.shouldSkipQuestion(userAnswer)) {
|
||||||
|
if (this.trueOrFalseService.getSkipped()) {
|
||||||
|
this.statistServ.incrementCounterOfSkip();
|
||||||
|
this.moveNextQuestionWithOutCheck();
|
||||||
|
this.trueOrFalseService.setSkipped(false);
|
||||||
|
} else {
|
||||||
|
this.popupServ.dontMoveWitNowAnswer();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const isAnswerCorrect = this.trueOrFalseService.checkAnswer(userAnswer);
|
||||||
|
this.evalueteAnswer(userAnswer, isAnswerCorrect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldSkipQuestion(userAnser: string): boolean {
|
||||||
|
return userAnser === undefined || userAnser.trim() === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
evalueteAnswer(userAnswer: string, isAnswerCorrect: boolean): void {
|
||||||
|
this.recordAnswerData(userAnswer, isAnswerCorrect);
|
||||||
|
|
||||||
|
if (isAnswerCorrect) {
|
||||||
|
this.statistServ.incrementCounterOfCorrect();
|
||||||
|
|
||||||
|
if (this.isLastQuestion) {
|
||||||
|
this.endExam();
|
||||||
|
} else {
|
||||||
|
this.forwardQuestion();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Answer is wrong
|
||||||
|
if (this.statistServ.getCounterOfIncorrect() === 6) {
|
||||||
|
this.popupServ.backToLearningCenterCheck();
|
||||||
|
} else{
|
||||||
|
this.statistServ.incrementCounterOfIncorrect();
|
||||||
|
this.popupServ.wrongAnswerMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isLastQuestion) {
|
||||||
|
this.popupServ.wrongAnswerMessage();
|
||||||
|
this.statistServ.incrementCounterOfIncorrect();
|
||||||
|
this.previusQuestion();
|
||||||
|
} else{
|
||||||
|
this.previusQuestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
forwardQuestion(): void {
|
||||||
|
const currentIndex = this.trueOrFalseService.getCurrentQuestionIndex();
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
this.trueOrFalseService.getCurrentQuestionIndex() ===
|
||||||
|
this.totalQuestions - 1
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.trueOrFalseService.incrementQuestionIndex();
|
||||||
|
this.isFirstQuestion = false;
|
||||||
|
this.isLastQuestion = currentIndex === this.totalQuestions - 1;
|
||||||
|
this.loadCurrentQuestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previusQuestion(): void {
|
||||||
|
const currentIndex = this.trueOrFalseService.getCurrentQuestionIndex();
|
||||||
|
if (currentIndex > 0 ) {
|
||||||
|
this.trueOrFalseService.decrementQuestionIndex();
|
||||||
|
this.isFirstQuestion = currentIndex === 1;
|
||||||
|
this.isLastQuestion = currentIndex === this.totalQuestions - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
moveNextQuestionWithOutCheck(): void {
|
||||||
|
if (!this.isLastQuestion) {
|
||||||
|
this.trueOrFalseService.incrementQuestionIndex();
|
||||||
|
this.isFirstQuestion = false;
|
||||||
|
this.isLastQuestion =
|
||||||
|
this.trueOrFalseService.getCurrentQuestionIndex() ===
|
||||||
|
this.totalQuestions - 1;
|
||||||
|
this.loadCurrentQuestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skipQuestion(): void {
|
||||||
|
if (!this.isLastQuestion) {
|
||||||
|
this.trueOrFalseService.setSkipped(true);
|
||||||
|
this.trueOrFalseService.updateCurrentAnswers({
|
||||||
|
selectedChoice: [],
|
||||||
|
userAnswer: '',
|
||||||
|
});
|
||||||
|
this.moveNextQuestionCheckMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
movePreviusQuestion(): void {
|
||||||
|
if (!this.isFirstQuestion) {
|
||||||
|
const currentAnswers = this.trueOrFalseService.getCurrentAnswers();
|
||||||
|
this.trueOrFalseService.updateCurrentAnswers(currentAnswers);
|
||||||
|
this.trueOrFalseService.decrementQuestionIndex();
|
||||||
|
this.isFirstQuestion =
|
||||||
|
this.trueOrFalseService.getCurrentQuestionIndex() === 0;
|
||||||
|
this.isLastQuestion = false;
|
||||||
|
this.loadCurrentQuestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
moveNextQuestionExamMode(): void {
|
||||||
|
const currentAnswers = this.trueOrFalseService.getCurrentAnswers();
|
||||||
|
const userAnswer = currentAnswers.userAnswer;
|
||||||
|
if (!this.isLastQuestion) {
|
||||||
|
//Überprüfen ob die Antwort korrekt ist
|
||||||
|
|
||||||
|
if (this.shouldSkipQuestion(userAnswer)) {
|
||||||
|
|
||||||
|
if (this.trueOrFalseService.getSkipped()) {
|
||||||
|
this.statistServ.incrementCounterOfSkip();
|
||||||
|
this.trueOrFalseService.setSkipped(false);
|
||||||
|
} else {
|
||||||
|
this.skipQuestion();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const isAnswerCorrect = this.trueOrFalseService.checkAnswer(userAnswer);
|
||||||
|
this.evalueteAnswerExam(userAnswer, isAnswerCorrect);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.shouldSkipQuestion(userAnswer)) {
|
||||||
|
if (this.trueOrFalseService.getSkipped()) {
|
||||||
|
this.statistServ.incrementCounterOfSkip();
|
||||||
|
this.trueOrFalseService.setSkipped(false);
|
||||||
|
} else {
|
||||||
|
this.skipQuestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.evalueteAnswerExam(userAnswer,this.trueOrFalseService.checkAnswer(userAnswer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evalueteAnswerExam(userAnswer: string, isAnswerCorrect: boolean): void {
|
||||||
|
this.recordAnswerData(userAnswer, isAnswerCorrect);
|
||||||
|
|
||||||
|
if (isAnswerCorrect) {
|
||||||
|
//Answer is correct
|
||||||
|
this.statistServ.incrementCounterOfCorrect();
|
||||||
|
} else {
|
||||||
|
//Answer is incorrect
|
||||||
|
this.statistServ.incrementCounterOfIncorrect();
|
||||||
|
this.twentyPercentThrsholdRule();
|
||||||
|
}
|
||||||
|
if(this.isLastQuestion){
|
||||||
|
this.endExam();
|
||||||
|
}else{
|
||||||
|
this.forwardQuestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
twentyPercentThrsholdRule(): void{
|
||||||
|
const incorrectAnswers = this.statistServ.getCounterOfIncorrect();
|
||||||
|
const totalQuestions = this.trueOrFalseService.getKatalog1().length;
|
||||||
|
const twentyPercentThrshold = totalQuestions * 0.2;
|
||||||
|
|
||||||
|
if (incorrectAnswers >= twentyPercentThrshold) {
|
||||||
|
this.popupServ.backToLearningCenterExam();
|
||||||
|
} else {
|
||||||
|
if (this.thisIsLastQuestion()) {
|
||||||
|
this.endExam();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endExam(): void {
|
||||||
|
this.popupSaticMessages()
|
||||||
|
}
|
||||||
|
|
||||||
|
thisIsLastQuestion(): boolean {
|
||||||
|
if (
|
||||||
|
this.trueOrFalseService.getCurrentQuestionIndex() ===
|
||||||
|
this.trueOrFalseService.getKatalog1().length - 1
|
||||||
|
) {
|
||||||
|
this.isLastQuestion = true;
|
||||||
|
return this.isLastQuestion;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thisIsFirstQuesteion(): boolean {
|
||||||
|
return this.isFirstQuestion;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentQuestion(): Question {
|
||||||
|
return this.trueOrFalseService.getKatalog1()[
|
||||||
|
this.trueOrFalseService.getCurrentQuestionIndex()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentQuestion(question: Question): void {
|
||||||
|
this.currentQuestion = question;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCurrentQuestion(): void {
|
||||||
|
const currentQuestion = this.trueOrFalseService.getCurrentQuestion();
|
||||||
|
if (currentQuestion) {
|
||||||
|
this.setCurrentQuestion(currentQuestion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
markQuestionAnswered(index: number): void {
|
||||||
|
if (index >= 0 && index < this.answeredQuestions.length) {
|
||||||
|
this.answeredQuestions[index] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasQuestionBeenAnswered(index: number): boolean {
|
||||||
|
if (index >= 0 && index < this.answeredQuestions.length) {
|
||||||
|
return this.answeredQuestions[index];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
popupSaticMessages():void{
|
||||||
|
const totalQuestions = this.trueOrFalseService.getKatalog1().length;
|
||||||
|
const correctCounter = this.statistServ.getCounterOfCorrect();
|
||||||
|
const incorrectCounter = this.statistServ.getCounterOfIncorrect();
|
||||||
|
const skipCount = this.statistServ.getCounterOfSkip();
|
||||||
|
|
||||||
|
const message = `Total number of questions: ${totalQuestions}\nCorrectly answered: ${correctCounter}\nIncorrectly answered: ${incorrectCounter}\nSkipped: ${skipCount}`;
|
||||||
|
this.popupServ.openStatisticPopup(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/app/services/question-toggle.service.ts
Normal file
67
src/app/services/question-toggle.service.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { TrueOrFalesService } from './true-or-false.service';
|
||||||
|
import { FrageOption } from '../Model/frage-option';
|
||||||
|
import { NavigationEnd, Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class QuestionToggleService {
|
||||||
|
private showAnswer: boolean = false;
|
||||||
|
private shuffleEnabled: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private trueFalse: TrueOrFalesService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
toggleChoiceSelectionSingle(choice: FrageOption) {
|
||||||
|
const currentQuestion = this.trueFalse.getCurrentQuestion();
|
||||||
|
if (currentQuestion) {
|
||||||
|
currentQuestion.choices.map((c) => (c.selected = false));
|
||||||
|
choice.selected = !choice.selected;
|
||||||
|
this.trueFalse.updateCurrentAnswers({
|
||||||
|
selectedChoice: currentQuestion.choices,
|
||||||
|
userAnswer: currentQuestion.answer as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleChoiceSelectionMulti(choice: FrageOption) {
|
||||||
|
const currentQuestion = this.trueFalse.getCurrentQuestion();
|
||||||
|
if (currentQuestion) {
|
||||||
|
choice.selected = !choice.selected;
|
||||||
|
|
||||||
|
const selectedChoices = currentQuestion.choices.filter((c) => c.selected);
|
||||||
|
|
||||||
|
this.trueFalse.updateCurrentAnswers({
|
||||||
|
selectedChoice: selectedChoices,
|
||||||
|
userAnswer: currentQuestion.answer as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleInputAnswer(answer: string | undefined) {
|
||||||
|
if (answer !== undefined) {
|
||||||
|
const currentQuestion = this.trueFalse.getCurrentQuestion();
|
||||||
|
if (currentQuestion) {
|
||||||
|
const normalizedUserAnswer = this.trueFalse.normalizeUserAnswer(answer);
|
||||||
|
if (normalizedUserAnswer !== undefined) {
|
||||||
|
this.trueFalse.setCurrentAnswers(normalizedUserAnswer);
|
||||||
|
} else {
|
||||||
|
console.log('Normalized answer is undefined.'); // Handle undefined normalized answer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('Answer is undefined.'); // Handle undefined answer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAnswerVisibility(): void{
|
||||||
|
this.showAnswer = !this.showAnswer;
|
||||||
|
}
|
||||||
|
getShowAnswer(): boolean{
|
||||||
|
return this.showAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
37
src/app/services/statistik.service.ts
Normal file
37
src/app/services/statistik.service.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class StatistikService {
|
||||||
|
private correct: number = 0;
|
||||||
|
private incorrect: number = 0;
|
||||||
|
private skip: number = 0;
|
||||||
|
|
||||||
|
incrementCounterOfCorrect(): void {
|
||||||
|
this.correct++;
|
||||||
|
}
|
||||||
|
incrementCounterOfIncorrect(): void {
|
||||||
|
this.incorrect++;
|
||||||
|
}
|
||||||
|
incrementCounterOfSkip(): void {
|
||||||
|
this.skip++;
|
||||||
|
}
|
||||||
|
getCounterOfCorrect(): number {
|
||||||
|
return this.correct;
|
||||||
|
}
|
||||||
|
getCounterOfIncorrect(): number {
|
||||||
|
return this.incorrect;
|
||||||
|
}
|
||||||
|
getCounterOfSkip(): number {
|
||||||
|
return this.skip;
|
||||||
|
}
|
||||||
|
resetStatistics(): void{
|
||||||
|
this.correct = 0;
|
||||||
|
this.incorrect = 0;
|
||||||
|
this.skip = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
206
src/app/services/true-or-false.service.ts
Normal file
206
src/app/services/true-or-false.service.ts
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { EMPTY, Observable, catchError,tap } from 'rxjs';
|
||||||
|
import { Question } from '../Model/question';
|
||||||
|
import { DataInput } from './data-input';
|
||||||
|
import { Answers } from '../Model/answers';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class TrueOrFalesService {
|
||||||
|
private katalog1: Question[] = [];
|
||||||
|
private katalog2: Question[] = [];
|
||||||
|
private answer: Answers[] = [];
|
||||||
|
private currentQuestionIndex: number = 0;
|
||||||
|
public isLastQuestion: boolean = true;
|
||||||
|
public isFirstQuestion: boolean = false;
|
||||||
|
private skipped: boolean = false;
|
||||||
|
private userAnswer:string = '';
|
||||||
|
private shuffleEnabled: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fragenService: DataInput,
|
||||||
|
private http: HttpClient,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeAnswer(): void {
|
||||||
|
this.answer = this.katalog1.map(() => ({
|
||||||
|
selectedChoice: [],
|
||||||
|
userAnswer: '',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
setQuestionsAndAnswers(questions: Question[]): void {
|
||||||
|
this.katalog1 = questions;
|
||||||
|
this.isFirstQuestion = true;
|
||||||
|
this.isLastQuestion = this.katalog1.length === 1;
|
||||||
|
this.initializeAnswer();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentQuestionIndex(): number {
|
||||||
|
return this.currentQuestionIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentQuestionIndex(index: number):void {
|
||||||
|
this.currentQuestionIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
incrementQuestionIndex():void{
|
||||||
|
this.currentQuestionIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
decrementQuestionIndex():void{
|
||||||
|
this.currentQuestionIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentQuestion(): Question {
|
||||||
|
return this.katalog1[this.getCurrentQuestionIndex()];
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentAnswers(): Answers {
|
||||||
|
const currentAnswers = this.answer[this.getCurrentQuestionIndex()];
|
||||||
|
return currentAnswers || {selectedChoice: [], userAnswer: ''}
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentAnswers(userAnswer: string): void {
|
||||||
|
this.setUserAnswer(userAnswer);
|
||||||
|
const currentAnswers = this.getCurrentAnswers();
|
||||||
|
this.updateCurrentAnswers(currentAnswers);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUserAnswer(userAnswer: string): void {
|
||||||
|
this.userAnswer = userAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUserAnswer(): string {
|
||||||
|
return this.userAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCurrentAnswers(updatedAnswers: Answers): void {
|
||||||
|
this.answer[this.getCurrentQuestionIndex()] = updatedAnswers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setKatalogAndQuestions(data: Question[]): void {
|
||||||
|
this.katalog1 = data;
|
||||||
|
this.currentQuestionIndex = 0;
|
||||||
|
this.isLastQuestion = false;
|
||||||
|
this.isFirstQuestion = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
loadKatalogAndQuestions(): Observable<Question[]> {
|
||||||
|
this.initializeAnswer()
|
||||||
|
return this.http
|
||||||
|
.get<Question[]>(this.fragenService.getKatalog1Url())
|
||||||
|
.pipe(
|
||||||
|
tap((data) => {
|
||||||
|
this.setKatalogAndQuestions(data);
|
||||||
|
}),
|
||||||
|
catchError((error) => {
|
||||||
|
console.log('Fehler beim Laden des Katalogs aufgetreten', error);
|
||||||
|
return EMPTY;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadKatalog2AndQuestions(): Observable<Question[]> {
|
||||||
|
this.initializeAnswer()
|
||||||
|
return this.http
|
||||||
|
.get<Question[]>(this.fragenService.getKaltalog2Url())
|
||||||
|
.pipe(
|
||||||
|
tap((data) => {
|
||||||
|
this.setKatalogAndQuestions(data);
|
||||||
|
}),
|
||||||
|
catchError((error) => {
|
||||||
|
console.log('Fehler beim laden des Katalaogs aufgetreten', error);
|
||||||
|
return EMPTY;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAnswer(userAnswer: string | string[]): boolean {
|
||||||
|
const currentQuestion = this.getCurrentQuestion();
|
||||||
|
|
||||||
|
//SingleChoiceQuestions
|
||||||
|
if (currentQuestion.questionType === 'single') {
|
||||||
|
return this.checkSingleChoiceAnswer(currentQuestion);
|
||||||
|
//MultipleChoiceQuestions
|
||||||
|
} else if (currentQuestion.questionType === 'multiple') {
|
||||||
|
return this.checkMultipleChoiceAnswer(currentQuestion);
|
||||||
|
//InputQuestions
|
||||||
|
} else if (currentQuestion.questionType === 'input') {
|
||||||
|
return this.checkInputAnswer(currentQuestion, userAnswer);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkSingleChoiceAnswer(question: Question): boolean{
|
||||||
|
const selectedChoice = this.getCurrentAnswers().selectedChoice.find(choice => choice.selected);
|
||||||
|
if (selectedChoice) {
|
||||||
|
const selectedFirstLatter = selectedChoice.text[0];
|
||||||
|
return selectedFirstLatter === question.answer;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkMultipleChoiceAnswer(qustion: Question): boolean{
|
||||||
|
const selectedChoices = this.getCurrentAnswers().selectedChoice.filter(choice => choice.selected);
|
||||||
|
const selectedTexts = selectedChoices.map(choice => choice.text[0]).sort().join('');
|
||||||
|
const sortedCorrectAnswer = qustion.answer.toString().split('').sort().join('');
|
||||||
|
return selectedTexts === sortedCorrectAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkInputAnswer(question: Question, userAnswer: string | string[]): boolean{
|
||||||
|
const formattedCorrectAnswers = Array.isArray(question.answer)
|
||||||
|
? question.answer.map(answer => answer.trim().toLowerCase())
|
||||||
|
: [question.answer.trim().toLowerCase()];
|
||||||
|
|
||||||
|
const normalizeUserAnswer = this.normalizeUserAnswer(userAnswer)||'';
|
||||||
|
const includes = formattedCorrectAnswers.includes(normalizeUserAnswer);
|
||||||
|
return includes;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Normalizsation
|
||||||
|
normalizeUserAnswer(userAnswer: string | string[] | boolean): string | undefined {
|
||||||
|
if (typeof userAnswer === 'boolean') {
|
||||||
|
return [userAnswer.toString()].join('');
|
||||||
|
} else if (Array.isArray(userAnswer)) {
|
||||||
|
return userAnswer.map((answer: string | boolean) => (typeof answer === 'string' ? answer.trim() : answer.toString())).join('');
|
||||||
|
} else if (userAnswer === undefined) {
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
return [userAnswer.trim()].join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compareInputAnswers(userAnswer: string, correctAnswers: string): boolean {
|
||||||
|
const formattedUserAnswer = userAnswer.trim().replace(/\r?\n|\r/g, '');
|
||||||
|
const formattedCorrectAnswers = correctAnswers
|
||||||
|
.trim()
|
||||||
|
.replace(/\r?\n|\r/g, '');
|
||||||
|
return (
|
||||||
|
formattedUserAnswer.toLowerCase() === formattedCorrectAnswers.toLowerCase()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSkipped(value: boolean): void{
|
||||||
|
this.skipped = value;
|
||||||
|
}
|
||||||
|
getSkipped():boolean{
|
||||||
|
return this.skipped;
|
||||||
|
}
|
||||||
|
getKatalog1(): Question[] {
|
||||||
|
return this.katalog1;
|
||||||
|
}
|
||||||
|
getKatalog2(): Question[] {
|
||||||
|
return this.katalog2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
26
src/app/shared/shared.module.ts
Normal file
26
src/app/shared/shared.module.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { SingleChoiceQuestionComponent } from '../Templates/single-choice-questeion/single-choice-questeion.component';
|
||||||
|
import { MultipleChoiceQuestionComponent } from '../Templates/multiple-choice-questeion/multiple-choice-questeion.component';
|
||||||
|
import { InputChoiceQuesteionComponent } from '../Templates/input-choice-questeion/input-choice-questeion.component';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
SingleChoiceQuestionComponent,
|
||||||
|
MultipleChoiceQuestionComponent,
|
||||||
|
InputChoiceQuesteionComponent,
|
||||||
|
],
|
||||||
|
exports:[
|
||||||
|
SingleChoiceQuestionComponent,
|
||||||
|
MultipleChoiceQuestionComponent,
|
||||||
|
InputChoiceQuesteionComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SharedModule { }
|
||||||
0
src/assets/.gitkeep
Normal file
0
src/assets/.gitkeep
Normal file
3131
src/assets/LPI-2019-1-101d-QA.json
Normal file
3131
src/assets/LPI-2019-1-101d-QA.json
Normal file
File diff suppressed because it is too large
Load Diff
2921
src/assets/LPI-2019-1-102d-QA.json
Normal file
2921
src/assets/LPI-2019-1-102d-QA.json
Normal file
File diff suppressed because it is too large
Load Diff
35
src/custom-theme.scss
Normal file
35
src/custom-theme.scss
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
// Custom Theming for Angular Material
|
||||||
|
// For more information: https://material.angular.io/guide/theming
|
||||||
|
@use '@angular/material' as mat;
|
||||||
|
// Plus imports for other components in your app.
|
||||||
|
|
||||||
|
// Include the common styles for Angular Material. We include this here so that you only
|
||||||
|
// have to load a single css file for Angular Material in your app.
|
||||||
|
// Be sure that you only ever include this mixin once!
|
||||||
|
@include mat.core();
|
||||||
|
|
||||||
|
// Define the palettes for your theme using the Material Design palettes available in palette.scss
|
||||||
|
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
|
||||||
|
// hue. Available color palettes: https://material.io/design/color/
|
||||||
|
$LLCE-primary: mat.define-palette(mat.$indigo-palette);
|
||||||
|
$LLCE-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400);
|
||||||
|
|
||||||
|
// The warn palette is optional (defaults to red).
|
||||||
|
$LLCE-warn: mat.define-palette(mat.$red-palette);
|
||||||
|
|
||||||
|
// Create the theme object. A theme consists of configurations for individual
|
||||||
|
// theming systems such as "color" or "typography".
|
||||||
|
$LLCE-theme: mat.define-light-theme((
|
||||||
|
color: (
|
||||||
|
primary: $LLCE-primary,
|
||||||
|
accent: $LLCE-accent,
|
||||||
|
warn: $LLCE-warn,
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
// Include theme styles for core and each component used in your app.
|
||||||
|
// Alternatively, you can import and @include the theme mixins for each component
|
||||||
|
// that you are using.
|
||||||
|
@include mat.all-component-themes($LLCE-theme);
|
||||||
|
|
||||||
BIN
src/favicon.ico
Normal file
BIN
src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 948 B |
16
src/index.html
Normal file
16
src/index.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>LLCE</title>
|
||||||
|
<base href="/">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="mat-typography">
|
||||||
|
<LL-root></LL-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
7
src/main.ts
Normal file
7
src/main.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
|
import { AppModule } from './app/app.module';
|
||||||
|
|
||||||
|
|
||||||
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
|
.catch(err => console.error(err));
|
||||||
132
src/styles.css
Normal file
132
src/styles.css
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
h1 {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
/* Rahmen um die Links */
|
||||||
|
border-radius: 5px;
|
||||||
|
/* Abrundung des Rahmens */
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: rgb(131, 169, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 20px;
|
||||||
|
/* Add some top margin for spacing */
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-section {
|
||||||
|
flex-grow: 1;
|
||||||
|
/* Stilisierung für den Bereich der Fragen oder des Inhalts hier */
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-container {
|
||||||
|
max-width: 700px;
|
||||||
|
width: 50%;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 20px auto;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-block {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stil für die Antwortmöglichkeiten */
|
||||||
|
.question-block ul {
|
||||||
|
list-style: disc;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stil für die richtige Antwort */
|
||||||
|
.question-block p:last-child {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-text{
|
||||||
|
width: 500px;
|
||||||
|
height: 85px;
|
||||||
|
border: 0px;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice-text{
|
||||||
|
width: 500;
|
||||||
|
height: 250px;
|
||||||
|
border: 0px;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoTextBox {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-infoTextBox {
|
||||||
|
margin-top: 100px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-answer-button {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background-color: #ddd;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-answer-button.selected {
|
||||||
|
background-color: rgb(131, 169, 250); /* Hintergrundfarbe für ausgewählten Button */
|
||||||
|
color: #fff; /* Textfarbe für ausgewählten Button */
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-answer-button:hover {
|
||||||
|
background-color: rgb(131, 169, 250); /* Hintergrundfarbe für gehoverten Button */
|
||||||
|
color: #fff; /* Textfarbe für gehoverten Button */
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-column {
|
||||||
|
width: 200px; /* Breite der leeren Spalte */
|
||||||
|
background-color: #f7f7f7; /* Hintergrundfarbe der leeren Spalte */
|
||||||
|
/* Weitere Stilisierung hier, z. B. Abstand oder Werbung einfügen */
|
||||||
|
}
|
||||||
|
|
||||||
14
tsconfig.app.json
Normal file
14
tsconfig.app.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-tsc/app",
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/main.ts"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"src/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
33
tsconfig.json
Normal file
33
tsconfig.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./",
|
||||||
|
"outDir": "./dist/out-tsc",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": false,
|
||||||
|
"downlevelIteration": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"importHelpers": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
"lib": [
|
||||||
|
"ES2022",
|
||||||
|
"dom"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
|
"strictInjectionParameters": true,
|
||||||
|
"strictInputAccessModifiers": true,
|
||||||
|
"strictTemplates": true
|
||||||
|
}
|
||||||
|
}
|
||||||
14
tsconfig.spec.json
Normal file
14
tsconfig.spec.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-tsc/spec",
|
||||||
|
"types": [
|
||||||
|
"jasmine"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user