BugFix bei Trainingsanzeige
This commit is contained in:
parent
576689a3ed
commit
f3cde1578e
|
|
@ -21,7 +21,9 @@
|
||||||
<span class="text-gray-600">({{ deck.images.length }} Pics)</span>
|
<span class="text-gray-600">({{ deck.images.length }} Pics)</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm text-gray-600">
|
<div class="text-sm text-gray-600">
|
||||||
<div [ngClass]="{ 'text-blue-500 font-bold': isToday(getNextTrainingDate(deck)) }">Next training: {{ getNextTrainingString(deck) }}</div>
|
<div [ngClass]="{ 'text-blue-500 font-bold': isToday(getNextTrainingDate(deck)), 'text-rose-500 font-bold': isBeforeToday(getNextTrainingDate(deck)) }">
|
||||||
|
Next training: {{ getNextTrainingString(deck) }}
|
||||||
|
</div>
|
||||||
<div>Words to review: {{ getWordsToReview(deck) }}</div>
|
<div>Words to review: {{ getWordsToReview(deck) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -97,15 +99,14 @@
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div class="flex flex-row space-x-2 items-stretch">
|
||||||
<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 flex items-center justify-center whitespace-nowrap">Start Training</button>
|
||||||
<button (click)="openTraining(activeDeck)" class="flex-1 bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600">Start Training</button>
|
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="relative">
|
<div class="relative h-full">
|
||||||
<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 h-full">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col items-center">
|
||||||
<div>Add Image</div>
|
<div class="whitespace-nowrap">Add Image</div>
|
||||||
<div class="text-xs">(from file)</div>
|
<div class="text-xs whitespace-nowrap">(from file)</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<input #imageFile type="file" id="imageFile" (change)="onFileChange($event)" accept="image/jpeg,image/png,image/gif,image/webp" required class="hidden" />
|
<input #imageFile type="file" id="imageFile" (change)="onFileChange($event)" accept="image/jpeg,image/png,image/gif,image/webp" required class="hidden" />
|
||||||
|
|
@ -113,10 +114,10 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- Neuer Button Paste Image -->
|
<!-- Neuer Button Paste Image -->
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<button (click)="pasteImage()" class="w-full bg-purple-500 text-white py-2 px-4 rounded hover:bg-purple-600">
|
<button (click)="pasteImage()" class="w-full bg-purple-500 text-white py-2 px-4 rounded hover:bg-purple-600 flex items-center justify-center h-full">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col items-center">
|
||||||
<div>Paste Image</div>
|
<div class="whitespace-nowrap">Paste Image</div>
|
||||||
<div class="text-xs">(from clipboard)</div>
|
<div class="text-xs whitespace-nowrap">(from clipboard)</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -493,31 +493,54 @@ export class DeckListComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methode zur Berechnung des nächsten Trainingsdatums
|
// Methode zur Berechnung des nächsten Trainingsdatums
|
||||||
getNextTrainingDate(deck: Deck): Date {
|
getNextTrainingDate(deck: Deck): number {
|
||||||
const now = new Date();
|
const today = this.getTodayInDays();
|
||||||
now.setHours(0, 0, 0, 0);
|
const dueDates = deck.images.flatMap(image => image.boxes.map(box => (box.due ? box.due : null)));
|
||||||
const dueDates = deck.images.flatMap(image => image.boxes.map(box => (box.due ? new Date(box.due * 86400000) : null)));
|
|
||||||
if (dueDates.includes(null)) {
|
if (dueDates.includes(null)) {
|
||||||
return now;
|
return today;
|
||||||
}
|
}
|
||||||
const futureDueDates = dueDates.filter(date => date && date >= now);
|
//const futureDueDates = dueDates.filter(date => date && date >= now);
|
||||||
if (futureDueDates.length > 0) {
|
if (dueDates.length > 0) {
|
||||||
const nextDate = futureDueDates.reduce((a, b) => (a < b ? a : b));
|
const nextDate = dueDates.reduce((a, b) => (a < b ? a : b));
|
||||||
return nextDate;
|
return nextDate;
|
||||||
}
|
}
|
||||||
return now;
|
return today;
|
||||||
}
|
}
|
||||||
getNextTrainingString(deck: Deck): string {
|
getNextTrainingString(deck: Deck): string {
|
||||||
return this.getNextTrainingDate(deck).toLocaleDateString();
|
return this.daysSinceEpochToLocalDateString(this.getNextTrainingDate(deck));
|
||||||
}
|
}
|
||||||
// In deiner Component TypeScript Datei
|
// In deiner Component TypeScript Datei
|
||||||
isToday(date: Date): boolean {
|
isToday(epochDays: number): boolean {
|
||||||
const today = new Date();
|
return this.getTodayInDays() - epochDays === 0;
|
||||||
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
|
}
|
||||||
|
isBeforeToday(epochDays: number): boolean {
|
||||||
|
return this.getTodayInDays() - epochDays > 0;
|
||||||
}
|
}
|
||||||
// Methode zur Berechnung der Anzahl der zu bearbeitenden Wörter
|
// Methode zur Berechnung der Anzahl der zu bearbeitenden Wörter
|
||||||
getWordsToReview(deck: Deck): number {
|
getWordsToReview(deck: Deck): number {
|
||||||
const nextTraining = this.getNextTrainingDate(deck);
|
// const nextTraining = this.getNextTrainingDate(deck);
|
||||||
return deck.images.flatMap(image => image.boxes.filter(box => (box.due && new Date(box.due * 86400000) <= new Date(nextTraining)) || !box.due)).length;
|
// return deck.images.flatMap(image => image.boxes.filter(box => (box.due && new Date(box.due * 86400000) <= new Date(nextTraining)) || !box.due)).length;
|
||||||
|
const today = this.getTodayInDays();
|
||||||
|
return deck.images.flatMap(image => image.boxes.filter(box => (box.due && box.due <= today) || !box.due)).length;
|
||||||
|
// this.currentImageData.boxes.filter(box => box.due === undefined || box.due <= today);
|
||||||
|
}
|
||||||
|
getTodayInDays(): number {
|
||||||
|
const epoch = new Date(1970, 0, 1); // Anki uses UNIX epoch
|
||||||
|
const today = new Date();
|
||||||
|
return Math.floor((today.getTime() - epoch.getTime()) / (1000 * 60 * 60 * 24));
|
||||||
|
}
|
||||||
|
daysSinceEpochToLocalDateString(days: number): string {
|
||||||
|
const msPerDay = 24 * 60 * 60 * 1000;
|
||||||
|
// Erstelle ein Datum, das den exakten UTC-Zeitpunkt (Mitternacht UTC) repräsentiert:
|
||||||
|
const utcDate = new Date(days * msPerDay);
|
||||||
|
|
||||||
|
// Formatiere das Datum: Mit timeZone: 'UTC' wird der UTC-Wert genutzt,
|
||||||
|
// aber das Ausgabeformat (z. B. "4.2.2025" oder "2/4/2025") richtet sich nach der Locale.
|
||||||
|
return new Intl.DateTimeFormat(undefined, {
|
||||||
|
timeZone: 'UTC',
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
}).format(utcDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,33 @@
|
||||||
<div class="mt-10">
|
<div class="mt-10 mx-auto max-w-5xl">
|
||||||
<h2 class="text-2xl font-bold mb-4">Training: {{ deck.name }}</h2>
|
<h2 class="text-2xl font-bold mb-4">Training: {{ deck.name }}</h2>
|
||||||
<div class="bg-white shadow rounded-lg p-6 flex flex-col items-center">
|
<div class="rounded-lg p-6 flex flex-col items-center">
|
||||||
<canvas #canvas class="mb-4 border max-h-[50vh]"></canvas>
|
<canvas #canvas class="mb-4 border max-h-[50vh]"></canvas>
|
||||||
|
|
||||||
<div class="flex space-x-4 mb-4">
|
<div class="flex space-x-4 mb-4">
|
||||||
<!-- Show Button -->
|
<!-- Show Button -->
|
||||||
<button
|
<button (click)="showText()" class="bg-green-500 disabled:bg-green-200 text-white py-2 px-4 rounded hover:bg-green-600" [disabled]="isShowingBox || currentBoxIndex >= boxesToReview.length">Show</button>
|
||||||
(click)="showText()"
|
|
||||||
class="bg-green-500 disabled:bg-green-200 text-white py-2 px-4 rounded hover:bg-green-600"
|
|
||||||
[disabled]="isShowingBox || currentBoxIndex >= boxesToReview.length"
|
|
||||||
>
|
|
||||||
Show
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Again Button -->
|
<!-- Again Button -->
|
||||||
<button
|
<button (click)="markAgain()" class="bg-orange-500 disabled:bg-orange-200 text-white py-2 px-4 rounded hover:bg-orange-600" [disabled]="!isShowingBox || currentBoxIndex >= boxesToReview.length">
|
||||||
(click)="markAgain()"
|
|
||||||
class="bg-orange-500 disabled:bg-orange-200 text-white py-2 px-4 rounded hover:bg-orange-600"
|
|
||||||
[disabled]="!isShowingBox || currentBoxIndex >= boxesToReview.length"
|
|
||||||
>
|
|
||||||
Again ({{ getNextInterval(currentBox, 'again') }})
|
Again ({{ getNextInterval(currentBox, 'again') }})
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Good Button -->
|
<!-- Good Button -->
|
||||||
<button
|
<button (click)="markGood()" class="bg-blue-500 disabled:bg-blue-200 text-white py-2 px-4 rounded hover:bg-blue-600" [disabled]="!isShowingBox || currentBoxIndex >= boxesToReview.length">
|
||||||
(click)="markGood()"
|
|
||||||
class="bg-blue-500 disabled:bg-blue-200 text-white py-2 px-4 rounded hover:bg-blue-600"
|
|
||||||
[disabled]="!isShowingBox || currentBoxIndex >= boxesToReview.length"
|
|
||||||
>
|
|
||||||
Good ({{ getNextInterval(currentBox, 'good') }})
|
Good ({{ getNextInterval(currentBox, 'good') }})
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Easy Button -->
|
<!-- Easy Button -->
|
||||||
<button
|
<button (click)="markEasy()" class="bg-green-500 disabled:bg-green-200 text-white py-2 px-4 rounded hover:bg-green-600" [disabled]="!isShowingBox || currentBoxIndex >= boxesToReview.length">
|
||||||
(click)="markEasy()"
|
|
||||||
class="bg-green-500 disabled:bg-green-200 text-white py-2 px-4 rounded hover:bg-green-600"
|
|
||||||
[disabled]="!isShowingBox || currentBoxIndex >= boxesToReview.length"
|
|
||||||
>
|
|
||||||
Easy ({{ getNextInterval(currentBox, 'easy') }})
|
Easy ({{ getNextInterval(currentBox, 'easy') }})
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Next Image Button -->
|
<!-- Next Image Button -->
|
||||||
<button
|
<button (click)="skipToNextImage()" class="bg-yellow-500 disabled:bg-yellow-200 text-white py-2 px-4 rounded hover:bg-yellow-600" [disabled]="currentImageIndex >= deck.images.length">Next Image</button>
|
||||||
(click)="skipToNextImage()"
|
|
||||||
class="bg-yellow-500 disabled:bg-yellow-200 text-white py-2 px-4 rounded hover:bg-yellow-600"
|
|
||||||
[disabled]="currentImageIndex >= deck.images.length"
|
|
||||||
>
|
|
||||||
Next Image
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="mt-2">{{ progress }}</p>
|
<p class="mt-2">{{ progress }}</p>
|
||||||
|
|
||||||
<button
|
<button (click)="closeTraining()" class="mt-4 text-gray-500 hover:text-gray-700 underline">End Training</button>
|
||||||
(click)="closeTraining()"
|
|
||||||
class="mt-4 text-gray-500 hover:text-gray-700 underline"
|
|
||||||
>
|
|
||||||
End Training
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue