#7 Anzeige nächstes Training, formatting
This commit is contained in:
parent
ad4b5de1d2
commit
d17578d123
|
|
@ -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
|
||||||
17
.prettierrc
17
.prettierrc
|
|
@ -1,3 +1,18 @@
|
||||||
{
|
{
|
||||||
"singleQuote": true
|
"arrowParens": "avoid",
|
||||||
|
"embeddedLanguageFormatting": "auto",
|
||||||
|
"htmlWhitespaceSensitivity": "css",
|
||||||
|
"insertPragma": false,
|
||||||
|
"jsxBracketSameLine": false,
|
||||||
|
"jsxSingleQuote": false,
|
||||||
|
"printWidth": 220,
|
||||||
|
"proseWrap": "always",
|
||||||
|
"quoteProps": "as-needed",
|
||||||
|
"requirePragma": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"useTabs": false,
|
||||||
|
"vueIndentScriptAndStyle": false
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"editor.suggestSelection": "first",
|
||||||
|
"vsintellicode.modify.editor.suggestSelection": "automaticallyOverrodeDefaultValue",
|
||||||
|
"explorer.confirmDelete": false,
|
||||||
|
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[html]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[json]": {
|
||||||
|
"editor.defaultFormatter": "vscode.json-language-features"
|
||||||
|
},
|
||||||
|
"[scss]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[jsonc]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": "explicit"
|
||||||
|
},
|
||||||
|
"prettier.printWidth": 240,
|
||||||
|
"git.autofetch": false,
|
||||||
|
"git.autorefresh": true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,21 +1,46 @@
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
|
||||||
import { DeckListComponent } from './deck-list.component';
|
import { DeckListComponent } from './deck-list.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
template: `
|
template: `
|
||||||
<div class="container mx-auto p-4">
|
<div *ngIf="!isLoggedIn" class="min-h-screen flex flex-col items-center justify-center bg-gradient-to-r from-blue-500 to-purple-600 text-white">
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="text-5xl font-bold mb-4">Master Your Vocabulary</h1>
|
||||||
|
<p class="text-xl mb-8">Learn smarter, not harder. Start your journey today.</p>
|
||||||
|
<button (click)="loginWithGoogle()" class="bg-white text-blue-600 px-6 py-3 rounded-lg shadow-lg hover:bg-gray-100 transition duration-300 flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||||
|
<path
|
||||||
|
fill="#FFC107"
|
||||||
|
d="M43.611 20.083H42V20H24v8h11.303c-1.649 4.657-6.08 8-11.303 8-6.627 0-12-5.373-12-12s5.373-12 12-12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 12.955 4 4 12.955 4 24s8.955 20 20 20 20-8.955 20-20c0-1.341-.138-2.65-.389-3.917z"
|
||||||
|
/>
|
||||||
|
<path fill="#FF3D00" d="M6.306 14.691l6.571 4.819C14.655 15.108 18.961 12 24 12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 16.318 4 9.656 8.337 6.306 14.691z" />
|
||||||
|
<path fill="#4CAF50" d="M24 44c5.166 0 9.86-1.977 13.409-5.192l-6.19-5.238A11.91 11.91 0 0124 36c-5.202 0-9.619-3.317-11.283-7.946l-6.522 5.025C9.505 39.556 16.227 44 24 44z" />
|
||||||
|
<path fill="#1976D2" d="M43.611 20.083H42V20H24v8h11.303a12.04 12.04 0 01-4.087 5.571l.003-.002 6.19 5.238C36.971 39.205 44 34 44 24c0-1.341-.138-2.65-.389-3.917z" />
|
||||||
|
</svg>
|
||||||
|
Login with Google
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="isLoggedIn" class="container mx-auto p-4">
|
||||||
<h1 class="text-3xl font-bold text-center mb-8">Vocabulary Training</h1>
|
<h1 class="text-3xl font-bold text-center mb-8">Vocabulary Training</h1>
|
||||||
<app-deck-list></app-deck-list>
|
<app-deck-list></app-deck-list>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, DeckListComponent]
|
imports: [CommonModule, DeckListComponent],
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'vocabulary-training';
|
isLoggedIn = false; // Zustand für den Login-Status
|
||||||
ngOnInit(): void {
|
|
||||||
|
// Mock-Funktion für Google Login
|
||||||
|
loginWithGoogle() {
|
||||||
|
// Hier würde die eigentliche Google Login-Logik stehen
|
||||||
|
// Zum Beispiel mit Angular Fire oder einer anderen Bibliothek
|
||||||
|
// Für dieses Beispiel simulieren wir den Login:
|
||||||
|
this.isLoggedIn = true;
|
||||||
|
console.log('Logged in with Google');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<!-- Two-column layout -->
|
<!-- Two-column layout -->
|
||||||
<div *ngIf="!trainingsDeck" class="flex flex-col md:flex-row gap-4 mx-auto max-w-5xl">
|
<div *ngIf="!trainingsDeck" class="flex flex-col md:flex-row gap-4 mx-auto max-w-5xl">
|
||||||
|
|
||||||
<!-- Left column: List of decks -->
|
<!-- Left column: List of decks -->
|
||||||
<div class="w-auto">
|
<div class="w-auto">
|
||||||
<div class="bg-white shadow rounded-lg p-4">
|
<div class="bg-white shadow rounded-lg p-4">
|
||||||
|
|
@ -15,14 +14,17 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<div *ngFor="let deck of decks"
|
<div *ngFor="let deck of decks" class="flex justify-between items-center p-2 rounded hover:bg-gray-300 cursor-pointer" [class.bg-blue-200]="isDeckActive(deck)" (click)="toggleDeckExpansion(deck)">
|
||||||
class="flex justify-between items-center p-2 rounded hover:bg-gray-300 cursor-pointer"
|
<div class="flex flex-col space-y-1">
|
||||||
[class.bg-blue-200]="isDeckActive(deck)"
|
|
||||||
(click)="toggleDeckExpansion(deck)">
|
|
||||||
<div class="flex items-center space-x-2 whitespace-nowrap">
|
<div class="flex items-center space-x-2 whitespace-nowrap">
|
||||||
<h3 class="font-medium">{{ deck.name }}</h3>
|
<h3 class="font-medium">{{ deck.name }}</h3>
|
||||||
<span class="text-gray-600">({{ deck.images.length }})</span>
|
<span class="text-gray-600">({{ deck.images.length }})</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="text-sm text-gray-600">
|
||||||
|
<div>Next training: {{ getNextTrainingString(deck) }}</div>
|
||||||
|
<div>Words to review: {{ getWordsToReview(deck) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button class="text-gray-500 hover:text-gray-700 focus:outline-none">
|
<button class="text-gray-500 hover:text-gray-700 focus:outline-none">
|
||||||
<svg *ngIf="!isDeckActive(deck)" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 transform rotate-0 transition-transform duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg *ngIf="!isDeckActive(deck)" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 transform rotate-0 transition-transform duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||||
|
|
@ -65,7 +67,7 @@
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<div class="relative group">
|
<div class="relative group">
|
||||||
<div class="absolute left-0 bottom-full mb-2 w-48 bg-white border border-gray-300 rounded shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-10 pointer-events-none">
|
<div class="absolute left-0 bottom-full mb-2 w-48 bg-white border border-gray-300 rounded shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-10 pointer-events-none">
|
||||||
<img src="/debug_images/{{image.id}}/thumbnail.jpg" alt="{{ image.name }}" class="w-full h-auto object-cover">
|
<img src="/debug_images/{{ image.id }}/thumbnail.jpg" alt="{{ image.name }}" class="w-full h-auto object-cover" />
|
||||||
</div>
|
</div>
|
||||||
<span class="font-medium cursor-pointer">{{ image.name }}</span>
|
<span class="font-medium cursor-pointer">{{ image.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -92,14 +94,10 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="flex flex-row space-x-2">
|
<div class="flex flex-row space-x-2">
|
||||||
<button (click)="openTraining(activeDeck)" class="flex-1 bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600">
|
<button (click)="openTraining(activeDeck)" class="flex-1 bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600">Start Training</button>
|
||||||
Start Training
|
|
||||||
</button>
|
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<label for="imageFile" class="flex justify-center items-center bg-green-500 text-white py-2 px-4 rounded hover:bg-green-600 cursor-pointer">
|
<label for="imageFile" class="flex justify-center items-center bg-green-500 text-white py-2 px-4 rounded hover:bg-green-600 cursor-pointer"> Add Image </label>
|
||||||
Add Image
|
|
||||||
</label>
|
|
||||||
<input #imageFile type="file" id="imageFile" (change)="onFileChange($event)" accept="image/*" required class="hidden" />
|
<input #imageFile type="file" id="imageFile" (change)="onFileChange($event)" accept="image/*" required class="hidden" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -122,14 +120,7 @@
|
||||||
<!-- TrainingComponent -->
|
<!-- TrainingComponent -->
|
||||||
<app-training *ngIf="trainingsDeck" [deck]="trainingsDeck" (close)="closeTraining()"></app-training>
|
<app-training *ngIf="trainingsDeck" [deck]="trainingsDeck" (close)="closeTraining()"></app-training>
|
||||||
<!-- MoveImageModalComponent -->
|
<!-- MoveImageModalComponent -->
|
||||||
<app-move-image-modal
|
<app-move-image-modal *ngIf="imageToMove" [image]="imageToMove.image" [sourceDeck]="imageToMove.sourceDeck" [decks]="decks" (moveCompleted)="onImageMoved()" (closed)="imageToMove = null"> </app-move-image-modal>
|
||||||
*ngIf="imageToMove"
|
|
||||||
[image]="imageToMove.image"
|
|
||||||
[sourceDeck]="imageToMove.sourceDeck"
|
|
||||||
[decks]="decks"
|
|
||||||
(moveCompleted)="onImageMoved()"
|
|
||||||
(closed)="imageToMove = null">
|
|
||||||
</app-move-image-modal>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Popover für die Löschbestätigung -->
|
<!-- Popover für die Löschbestätigung -->
|
||||||
<div id="deletePopover" popover="manual" class="popover">
|
<div id="deletePopover" popover="manual" class="popover">
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { ChangeDetectorRef, Component, ElementRef, OnInit, signal, ViewChild, WritableSignal } from '@angular/core';
|
|
||||||
import { DeckService, Deck, DeckImage, Box, OcrResult } from './deck.service';
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { CreateDeckModalComponent } from './create-deck-modal/create-deck-modal.component';
|
import { CreateDeckModalComponent } from './create-deck-modal/create-deck-modal.component';
|
||||||
|
import { Box, Deck, DeckImage, DeckService, OcrResult } from './deck.service';
|
||||||
|
import { EditImageModalComponent } from './edit-image-modal/edit-image-modal.component';
|
||||||
|
import { MoveImageModalComponent } from './move-image-modal/move-image-modal.component';
|
||||||
import { TrainingComponent } from './training/training.component';
|
import { TrainingComponent } from './training/training.component';
|
||||||
import { UploadImageModalComponent } from './upload-image-modal/upload-image-modal.component';
|
import { UploadImageModalComponent } from './upload-image-modal/upload-image-modal.component';
|
||||||
import { EditImageModalComponent } from './edit-image-modal/edit-image-modal.component';
|
|
||||||
import { firstValueFrom } from 'rxjs';
|
|
||||||
import { MoveImageModalComponent } from './move-image-modal/move-image-modal.component';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-deck-list',
|
selector: 'app-deck-list',
|
||||||
|
|
@ -30,8 +30,8 @@ import { HttpClient } from '@angular/common/http';
|
||||||
TrainingComponent,
|
TrainingComponent,
|
||||||
EditImageModalComponent,
|
EditImageModalComponent,
|
||||||
MoveImageModalComponent, // Adding the new component
|
MoveImageModalComponent, // Adding the new component
|
||||||
UploadImageModalComponent
|
UploadImageModalComponent,
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
export class DeckListComponent implements OnInit {
|
export class DeckListComponent implements OnInit {
|
||||||
decks: Deck[] = [];
|
decks: Deck[] = [];
|
||||||
|
|
@ -41,19 +41,24 @@ export class DeckListComponent implements OnInit {
|
||||||
deckToRename: Deck | null = null;
|
deckToRename: Deck | null = null;
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
|
|
||||||
@ViewChild(CreateDeckModalComponent) createDeckModal!: CreateDeckModalComponent;
|
@ViewChild(CreateDeckModalComponent)
|
||||||
@ViewChild(UploadImageModalComponent) uploadImageModal!: UploadImageModalComponent;
|
createDeckModal!: CreateDeckModalComponent;
|
||||||
|
@ViewChild(UploadImageModalComponent)
|
||||||
|
uploadImageModal!: UploadImageModalComponent;
|
||||||
@ViewChild(EditImageModalComponent) editModal!: EditImageModalComponent;
|
@ViewChild(EditImageModalComponent) editModal!: EditImageModalComponent;
|
||||||
@ViewChild(UploadImageModalComponent) uploadModal!: UploadImageModalComponent;
|
@ViewChild(UploadImageModalComponent) uploadModal!: UploadImageModalComponent;
|
||||||
@ViewChild('imageFile') imageFileElement!: ElementRef;
|
@ViewChild('imageFile') imageFileElement!: ElementRef;
|
||||||
|
|
||||||
imageData: { imageSrc: string | ArrayBuffer | null, deckImage: DeckImage } | null = null;
|
imageData: {
|
||||||
|
imageSrc: string | ArrayBuffer | null;
|
||||||
|
deckImage: DeckImage;
|
||||||
|
} | null = null;
|
||||||
|
|
||||||
// Set to track expanded decks
|
// Set to track expanded decks
|
||||||
expandedDecks: Set<string> = new Set<string>();
|
expandedDecks: Set<string> = new Set<string>();
|
||||||
|
|
||||||
// State for moving images
|
// State for moving images
|
||||||
imageToMove: { image: DeckImage, sourceDeck: Deck } | null = null;
|
imageToMove: { image: DeckImage; sourceDeck: Deck } | null = null;
|
||||||
|
|
||||||
constructor(private deckService: DeckService, private cdr: ChangeDetectorRef, private http: HttpClient) {}
|
constructor(private deckService: DeckService, private cdr: ChangeDetectorRef, private http: HttpClient) {}
|
||||||
|
|
||||||
|
|
@ -64,14 +69,14 @@ export class DeckListComponent implements OnInit {
|
||||||
|
|
||||||
loadDecks(): void {
|
loadDecks(): void {
|
||||||
this.deckService.getDecks().subscribe({
|
this.deckService.getDecks().subscribe({
|
||||||
next: (data) => {
|
next: data => {
|
||||||
this.decks = data;
|
this.decks = data;
|
||||||
// Set the first deck as active if none is selected
|
// Set the first deck as active if none is selected
|
||||||
if (!this.activeDeck && this.decks.length > 0) {
|
if (!this.activeDeck && this.decks.length > 0) {
|
||||||
this.activeDeck = this.decks[0];
|
this.activeDeck = this.decks[0];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: (err) => console.error('Error loading decks', err)
|
error: err => console.error('Error loading decks', err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +108,7 @@ export class DeckListComponent implements OnInit {
|
||||||
this.closeDeletePopover();
|
this.closeDeletePopover();
|
||||||
this.activeDeck = this.decks[0];
|
this.activeDeck = this.decks[0];
|
||||||
},
|
},
|
||||||
error: (err) => console.error('Error deleting deck', err),
|
error: err => console.error('Error deleting deck', err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -154,7 +159,7 @@ export class DeckListComponent implements OnInit {
|
||||||
this.loadDecks(); // Lade die Decks neu, um die Liste zu aktualisieren
|
this.loadDecks(); // Lade die Decks neu, um die Liste zu aktualisieren
|
||||||
this.closeRenamePopover();
|
this.closeRenamePopover();
|
||||||
},
|
},
|
||||||
error: (err) => console.error('Error renaming deck', err),
|
error: err => console.error('Error renaming deck', err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +186,7 @@ export class DeckListComponent implements OnInit {
|
||||||
}
|
}
|
||||||
this.deckService.deleteDeck(deckName).subscribe({
|
this.deckService.deleteDeck(deckName).subscribe({
|
||||||
next: () => this.loadDecks(),
|
next: () => this.loadDecks(),
|
||||||
error: (err) => console.error('Error deleting deck', err)
|
error: err => console.error('Error deleting deck', err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,7 +196,7 @@ export class DeckListComponent implements OnInit {
|
||||||
if (newDeckName && newDeckName !== deck.name) {
|
if (newDeckName && newDeckName !== deck.name) {
|
||||||
this.deckService.renameDeck(deck.name, newDeckName).subscribe({
|
this.deckService.renameDeck(deck.name, newDeckName).subscribe({
|
||||||
next: () => this.loadDecks(),
|
next: () => this.loadDecks(),
|
||||||
error: (err) => console.error('Error renaming deck', err)
|
error: err => console.error('Error renaming deck', err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -212,7 +217,7 @@ export class DeckListComponent implements OnInit {
|
||||||
this.cdr.detectChanges(); // Erzwingt einen UI-Update
|
this.cdr.detectChanges(); // Erzwingt einen UI-Update
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: (err) => console.error('Error deleting image', err)
|
error: err => console.error('Error deleting image', err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -340,7 +345,7 @@ export class DeckListComponent implements OnInit {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = async (e) => {
|
reader.onload = async e => {
|
||||||
const imageSrc = e.target?.result;
|
const imageSrc = e.target?.result;
|
||||||
|
|
||||||
// Image as Base64 string without prefix (data:image/...)
|
// Image as Base64 string without prefix (data:image/...)
|
||||||
|
|
@ -391,4 +396,26 @@ export class DeckListComponent implements OnInit {
|
||||||
this.imageFileElement.nativeElement.value = '';
|
this.imageFileElement.nativeElement.value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Methode zur Berechnung des nächsten Trainingsdatums
|
||||||
|
getNextTrainingDate(deck: Deck): Date {
|
||||||
|
const now = new Date();
|
||||||
|
now.setHours(0, 0, 0, 0);
|
||||||
|
const dueDates = deck.images.flatMap(image => image.boxes.map(box => (box.due ? new Date(box.due * 86400000) : null)));
|
||||||
|
const futureDueDates = dueDates.filter(date => date && date >= now);
|
||||||
|
if (futureDueDates.length > 0) {
|
||||||
|
const nextDate = futureDueDates.reduce((a, b) => (a < b ? a : b));
|
||||||
|
return nextDate;
|
||||||
|
}
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
getNextTrainingString(deck: Deck): string {
|
||||||
|
return this.getNextTrainingDate(deck).toLocaleDateString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methode zur Berechnung der Anzahl der zu bearbeitenden Wörter
|
||||||
|
getWordsToReview(deck: Deck): number {
|
||||||
|
const nextTraining = this.getNextTrainingDate(deck);
|
||||||
|
return deck.images.flatMap(image => image.boxes.filter(box => box.due && new Date(box.due * 86400000) <= new Date(nextTraining))).length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue