From ae9017020c43267c3bd6b668ef6b2a2116353d2f Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 31 Jan 2025 19:01:10 +0100 Subject: [PATCH] logo first try, #20 cleanup, show today in blue deck-list --- api/src/app/drizzle.service.ts | 17 ++++++++++ api/src/app/proxy.controller.ts | 49 +++++++++++++++++++++++++++- public/favicon-16x16.png | Bin 0 -> 993 bytes public/favicon-32x32.png | Bin 0 -> 1941 bytes public/favicon-48x48.png | Bin 0 -> 3306 bytes src/app/app.component.ts | 6 ++-- src/app/deck-list.component.html | 40 +---------------------- src/app/deck-list.component.ts | 9 +++++- src/assets/logo.svg | 54 +++++++++++++++++++++++++++++++ src/index.html | 31 +++++++++++------- 10 files changed, 151 insertions(+), 55 deletions(-) create mode 100644 public/favicon-16x16.png create mode 100644 public/favicon-32x32.png create mode 100644 public/favicon-48x48.png create mode 100644 src/assets/logo.svg diff --git a/api/src/app/drizzle.service.ts b/api/src/app/drizzle.service.ts index ef6cb1a..e99977d 100644 --- a/api/src/app/drizzle.service.ts +++ b/api/src/app/drizzle.service.ts @@ -249,4 +249,21 @@ export class DrizzleService { return { status: 'success' }; } + + /** + * Methode zum Abrufen aller eindeutigen Bild-IDs aus der Datenbank + */ + async getDistinctBildIds(user: User): Promise { + try { + const result = await this.db.selectDistinct([Deck.bildid]).from(Deck).all(); + + // Extrahiere die bildid Werte aus dem Ergebnis + const usedIds = result.map((row: any) => row['0']).filter((id: string | null) => id !== null) as string[]; + console.log(usedIds); + return usedIds; + } catch (error) { + this.sqlLogger.logQuery('Error fetching distinct bildids', []); + throw new HttpException(`Fehler beim Abrufen der Bild-IDs - ${error}`, HttpStatus.INTERNAL_SERVER_ERROR); + } + } } diff --git a/api/src/app/proxy.controller.ts b/api/src/app/proxy.controller.ts index 9fbbe42..c7f3a04 100644 --- a/api/src/app/proxy.controller.ts +++ b/api/src/app/proxy.controller.ts @@ -2,11 +2,12 @@ import { Body, Controller, HttpException, HttpStatus, Post, Res, UseGuards } from '@nestjs/common'; import express from 'express'; import { AuthGuard } from '../service/auth.guard'; +import { DrizzleService } from './drizzle.service'; @Controller('') @UseGuards(AuthGuard) export class ProxyController { - constructor() {} + constructor(private readonly drizzleService: DrizzleService) {} // -------------------- // Proxy Endpoints // -------------------- @@ -54,4 +55,50 @@ export class ProxyController { throw new HttpException('Internal server error', HttpStatus.INTERNAL_SERVER_ERROR); } } + + // -------------------- + // Cleanup Endpoint + // -------------------- + @Post('cleanup') + async cleanupEndpoint(@Body() data: { dryrun?: boolean }, @Res() res: express.Response) { + try { + const user = res.req['user']; // Benutzerinformationen aus dem Request + + // 2. Nur Benutzer mit der spezifischen E-Mail dürfen fortfahren + if (user.email !== 'andreas.knuth@gmail.com') { + throw new HttpException('Zugriff verweigert.', HttpStatus.FORBIDDEN); + } + + // 1. Abrufen der distinct bildid aus der Datenbank + const usedIds = await this.drizzleService.getDistinctBildIds(user); + // 3. Verarbeitung des dryrun Parameters + const dryrun = data.dryrun !== undefined ? data.dryrun : true; + if (typeof dryrun !== 'boolean') { + throw new HttpException("'dryrun' muss ein boolescher Wert sein.", HttpStatus.BAD_REQUEST); + } + + // 4. Aufruf des Flask-Backend-Endpunkts + const response = await fetch('http://localhost:5000/api/cleanup', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ dryrun, usedIds }), + }); + + const result = await response.json(); + + if (!response.ok) { + throw new HttpException(result.error || 'Cleanup failed', response.status); + } + + // 5. Rückgabe der Ergebnisse an den Client + return res.status(HttpStatus.OK).json(result); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException('Interner Serverfehler', HttpStatus.INTERNAL_SERVER_ERROR); + } + } } diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..6999bab83ecb455ce141fb4be949d016d4a6dd10 GIT binary patch literal 993 zcmZ{idrVVz6vw~6d)s?^Z+lC5wGqtZmkX|LMxys((16X z1$J|4sj_l=LM7PMCivyE6{xDf$$k-#L~DD$;@DU3pZq7JDmU zQo}^!njSI&xxb5U+8|FKpzKcB$r(~%5p}Y$T2KVl3@Uj!*F8-YtT4)vQX-{*kpkuM zQIA9gj$YUVLJ;I}un@A9cDl4WQGmmGI25#lrH?AnBLk^#poX`moG4UbEf1>#LimJ< z$6+H_{sES+Fw%r-XqWxeTl}Z?Ss2iq?0y!e0bYQA<%hWMF${9kh@#hO9 zwH|F+JW~Uv&@IzgdrUYg!W?&*W3FQ+bQORyR5IbA-5oIK@L&#Xxpc1UB&{qdSCOO< z$ug94klUKrmH|eF**!1{Sb7u!1FkH>TN5f*R$kJO(=|(L%?`8d*+Jy6h_AwPLF95! zYqT_@sxy*!BYM0j*Q3=2Lq4uPj;IUi4e-}tsdwS_{kv{{xW$Wu@|Aum;YR%Nw4~Bw zNcL2qxlsz(Od#lEZ<4ZH=W1(I7?MiM0dG^J=+nN$Ri^0lsd((mFTNXBhpM-icCUSs zG%0%gF>j>pF;BR4<9hA+?ofAAM@;=cQeL3Wlxv;^)t7+W7TXYupR- zFNc1+`O8l)iaS5~!PeRP!t$B=C$3$OU!2R%5ZvNxZu~mDv?wiS$@pTK{@zT|-8Y() z1P;;RtP<^2wf2bJ<#yJ(>*`mDqFWSy->>=jU%>E={=w9q{|EMc`{p(Yu>Lb4GdP?~ dXL^T6{>wOBe-F8$ojffu2>3$Yw>^o;zW{dR_3Quu literal 0 HcmV?d00001 diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..bbf06d8518b3c31f3e6b067d70fef96dc81a7141 GIT binary patch literal 1941 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 Mfth~2u1oAc0ef#77XSbN literal 0 HcmV?d00001 diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 9dee6b9..51d1cac 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -11,8 +11,8 @@ import { PopoverService } from './services/popover.service'; template: `
-

Master Your Vocabulary

-

Learn smarter, not harder. Start your journey today.

+

Master Your Learning

+

Learn smarter, not harder. Start your journey today

- - - - - - - diff --git a/src/app/deck-list.component.ts b/src/app/deck-list.component.ts index 9c743e9..3234c59 100644 --- a/src/app/deck-list.component.ts +++ b/src/app/deck-list.component.ts @@ -373,6 +373,9 @@ export class DeckListComponent implements OnInit { 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))); + if (dueDates.includes(null)) { + return now; + } const futureDueDates = dueDates.filter(date => date && date >= now); if (futureDueDates.length > 0) { const nextDate = futureDueDates.reduce((a, b) => (a < b ? a : b)); @@ -383,7 +386,11 @@ export class DeckListComponent implements OnInit { getNextTrainingString(deck: Deck): string { return this.getNextTrainingDate(deck).toLocaleDateString(); } - + // In deiner Component TypeScript Datei + isToday(date: Date): boolean { + const today = new Date(); + return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear(); + } // Methode zur Berechnung der Anzahl der zu bearbeitenden Wörter getWordsToReview(deck: Deck): number { const nextTraining = this.getNextTrainingDate(deck); diff --git a/src/assets/logo.svg b/src/assets/logo.svg new file mode 100644 index 0000000..9d9d1a8 --- /dev/null +++ b/src/assets/logo.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/index.html b/src/index.html index 8b958c3..1dff7eb 100644 --- a/src/index.html +++ b/src/index.html @@ -1,13 +1,22 @@ - + - - - Vokabeltraining - - - - - - - + + + Vokabeltraining + + + + + + + + + +