Learn smarter, not harder. Start your journey today.
+
+
+
+
+
Vocabulary Training
`,
standalone: true,
- imports: [CommonModule, DeckListComponent]
+ imports: [CommonModule, DeckListComponent],
})
export class AppComponent {
- title = 'vocabulary-training';
- ngOnInit(): void {
+ isLoggedIn = false; // Zustand für den Login-Status
+
+ // 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');
}
-}
\ No newline at end of file
+}
diff --git a/src/app/deck-list.component.html b/src/app/deck-list.component.html
index 5aa9f72..8ee4bef 100644
--- a/src/app/deck-list.component.html
+++ b/src/app/deck-list.component.html
@@ -1,29 +1,31 @@
-
-
Decks
-
-
+
Decks
+
+
-
-
-
{{ deck.name }}
- ({{ deck.images.length }})
+
+
+
+
{{ deck.name }}
+ ({{ deck.images.length }})
+
+
+
Next training: {{ getNextTrainingString(deck) }}
+
Words to review: {{ getWordsToReview(deck) }}
+
-
@@ -122,14 +120,7 @@
-
-
+
@@ -148,4 +139,4 @@
RenameCancel
-
\ No newline at end of file
+
diff --git a/src/app/deck-list.component.ts b/src/app/deck-list.component.ts
index fc60c9c..4113bb6 100644
--- a/src/app/deck-list.component.ts
+++ b/src/app/deck-list.component.ts
@@ -1,19 +1,19 @@
-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 { 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 { 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 { 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({
selector: 'app-deck-list',
templateUrl: './deck-list.component.html',
standalone: true,
- styles:`
+ styles: `
.popover {
padding: 1rem;
border: 1px solid #ccc;
@@ -21,7 +21,7 @@ import { HttpClient } from '@angular/common/http';
background-color: white;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
max-width: 300px;
- }
+ }
`,
imports: [
CommonModule,
@@ -30,8 +30,8 @@ import { HttpClient } from '@angular/common/http';
TrainingComponent,
EditImageModalComponent,
MoveImageModalComponent, // Adding the new component
- UploadImageModalComponent
- ]
+ UploadImageModalComponent,
+ ],
})
export class DeckListComponent implements OnInit {
decks: Deck[] = [];
@@ -41,21 +41,26 @@ export class DeckListComponent implements OnInit {
deckToRename: Deck | null = null;
loading: boolean = false;
- @ViewChild(CreateDeckModalComponent) createDeckModal!: CreateDeckModalComponent;
- @ViewChild(UploadImageModalComponent) uploadImageModal!: UploadImageModalComponent;
+ @ViewChild(CreateDeckModalComponent)
+ createDeckModal!: CreateDeckModalComponent;
+ @ViewChild(UploadImageModalComponent)
+ uploadImageModal!: UploadImageModalComponent;
@ViewChild(EditImageModalComponent) editModal!: EditImageModalComponent;
@ViewChild(UploadImageModalComponent) uploadModal!: UploadImageModalComponent;
@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
expandedDecks: Set = new Set();
// 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) {}
ngOnInit(): void {
this.loadExpandedDecks();
@@ -64,14 +69,14 @@ export class DeckListComponent implements OnInit {
loadDecks(): void {
this.deckService.getDecks().subscribe({
- next: (data) => {
+ next: data => {
this.decks = data;
// Set the first deck as active if none is selected
if (!this.activeDeck && this.decks.length > 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.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.closeRenamePopover();
},
- error: (err) => console.error('Error renaming deck', err),
+ error: err => console.error('Error renaming deck', err),
});
}
}
@@ -181,21 +186,21 @@ export class DeckListComponent implements OnInit {
}
this.deckService.deleteDeck(deckName).subscribe({
next: () => this.loadDecks(),
- error: (err) => console.error('Error deleting deck', err)
+ error: err => console.error('Error deleting deck', err),
});
}
-
+
// Method to rename a deck
renameDeck(deck: Deck): void {
const newDeckName = prompt('Enter the new name for the deck:', deck.name);
if (newDeckName && newDeckName !== deck.name) {
this.deckService.renameDeck(deck.name, newDeckName).subscribe({
next: () => this.loadDecks(),
- error: (err) => console.error('Error renaming deck', err)
+ error: err => console.error('Error renaming deck', err),
});
}
}
-
+
// Method to delete an image from a deck
deleteImage(deck: Deck, image: DeckImage): void {
if (!confirm(`Are you sure you want to delete the image "${image.name}"?`)) {
@@ -212,7 +217,7 @@ export class DeckListComponent implements OnInit {
this.cdr.detectChanges(); // Erzwingt einen UI-Update
}
},
- error: (err) => console.error('Error deleting image', err)
+ error: err => console.error('Error deleting image', err),
});
}
@@ -305,10 +310,10 @@ export class DeckListComponent implements OnInit {
async onImageSaved() {
// Handle saving the image data, e.g., update the list of images
this.imageData = null;
-
+
// Lade die Decks neu
this.decks = await firstValueFrom(this.deckService.getDecks());
-
+
// Aktualisiere den activeDeck, falls dieser der aktuelle Deck ist
if (this.activeDeck) {
const updatedDeck = this.decks.find(deck => deck.name === this.activeDeck?.name);
@@ -335,12 +340,12 @@ export class DeckListComponent implements OnInit {
const fileNameElement = document.getElementById('fileName');
if (fileNameElement) {
fileNameElement.textContent = file.name;
- }
+ }
// this.imageFile = file;
this.loading = true;
const reader = new FileReader();
- reader.onload = async (e) => {
+ reader.onload = async e => {
const imageSrc = e.target?.result;
// Image as Base64 string without prefix (data:image/...)
@@ -382,13 +387,35 @@ export class DeckListComponent implements OnInit {
}
};
reader.readAsDataURL(file);
- }
+ }
/**
- * Resets the file input field so the same file can be selected again.
- */
+ * Resets the file input field so the same file can be selected again.
+ */
resetFileInput(): void {
if (this.imageFileElement && this.imageFileElement.nativeElement) {
this.imageFileElement.nativeElement.value = '';
}
}
-}
\ No newline at end of file
+
+ // 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;
+ }
+}