logging#19, max 5MB#18,addinsert/update#14,drizzle

This commit is contained in:
Your Name
2025-01-30 16:16:20 +01:00
parent 9f25253ade
commit 5707d1bb1f
22 changed files with 1073 additions and 100 deletions

View File

@@ -50,6 +50,7 @@ import { PopoverService } from './services/popover.service';
[title]="popoverTitle"
[message]="popoverMessage"
[showInput]="popoverShowInput"
[showCancel]="popoverShowCancel"
[inputValue]="popoverInputValue"
[confirmText]="popoverConfirmText"
(confirmed)="handleConfirm($event)"
@@ -103,6 +104,7 @@ export class AppComponent {
popoverTitle = '';
popoverMessage = '';
popoverShowInput = false;
popoverShowCancel = true;
popoverInputValue = '';
popoverConfirmText = 'Confirm';
private confirmCallback?: (inputValue?: string) => void;
@@ -114,6 +116,7 @@ export class AppComponent {
this.popoverTitle = options.title;
this.popoverMessage = options.message;
this.popoverShowInput = options.showInput;
this.popoverShowCancel = options.showCancel;
this.popoverInputValue = options.inputValue;
this.popoverConfirmText = options.confirmText;
this.confirmCallback = options.onConfirm;

View File

@@ -17,7 +17,9 @@ import { FormsModule } from '@angular/forms';
<input *ngIf="showInput" type="text" class="w-full p-2 border rounded mb-4 focus:ring-2 focus:ring-blue-500 focus:border-transparent" [(ngModel)]="inputValue" (keyup.enter)="onConfirm()" autofocus />
<div class="flex justify-end space-x-2">
@if(showCancel){
<button (click)="onCancel()" class="px-4 py-2 rounded bg-gray-100 text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400">Cancel</button>
}
<button (click)="onConfirm()" class="px-4 py-2 rounded bg-blue-500 text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500">
{{ confirmText }}
</button>
@@ -30,6 +32,7 @@ export class PopoverComponent {
@Input() title: string = '';
@Input() message: string = '';
@Input() showInput: boolean = false;
@Input() showCancel: boolean = false;
@Input() confirmText: string = 'Confirm';
@Input() inputValue: string = '';
@Input() visible: boolean = false;

View File

@@ -67,7 +67,7 @@
<div class="flex items-center space-x-2">
<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">
<img src="/debug_images/{{ image.id }}/thumbnail.jpg" alt="{{ image.name }}" class="w-full h-auto object-cover" />
<img src="/debug_images/{{ image.bildid }}/thumbnail.jpg" alt="{{ image.name }}" class="w-full h-auto object-cover" />
</div>
<span class="font-medium cursor-pointer">{{ image.name }}</span>
</div>
@@ -98,7 +98,7 @@
<div class="flex-1">
<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"> 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/jpeg,image/png,image/gif,image/webp" required class="hidden" />
</div>
</div>
</div>

View File

@@ -144,18 +144,19 @@ export class DeckListComponent implements OnInit {
title: 'Delete Image',
message: `Are you sure you want to delete the image ${image.name}?`,
confirmText: 'Delete',
showCancel: true,
onConfirm: () => this.confirmImageDelete(deck, image),
});
}
confirmImageDelete(deck: Deck, image: DeckImage): void {
const imageId = image.id;
const imageId = image.bildid;
this.deckService.deleteImage(imageId).subscribe({
next: () => {
this.loadDecks();
if (this.activeDeck) {
this.activeDeck.images = this.activeDeck.images.filter(img => img.id !== imageId);
this.activeDeck.images = this.activeDeck.images.filter(img => img.bildid !== imageId);
this.cdr.detectChanges();
}
},
@@ -166,7 +167,7 @@ export class DeckListComponent implements OnInit {
// Method to edit an image in a deck
editImage(deck: Deck, image: DeckImage): void {
let imageSrc = null;
fetch(`/debug_images/${image.id}/original_compressed.jpg`)
fetch(`/debug_images/${image.bildid}/original_compressed.jpg`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
@@ -282,11 +283,36 @@ export class DeckListComponent implements OnInit {
onFileChange(event: any): void {
const file: File = event.target.files[0];
if (!file) return;
// Erlaubte Dateitypen
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
// Prüfe den Dateityp
if (!allowedTypes.includes(file.type)) {
this.popoverService.show({
title: 'Information',
message: 'Only JPG, PNG, GIF and WebP images are allowed',
});
this.resetFileInput();
return;
}
// Prüfe die Dateigröße (5MB = 5 * 1024 * 1024 Bytes)
const maxSize = 5 * 1024 * 1024; // 5MB in Bytes
if (file.size > maxSize) {
this.popoverService.show({
title: 'Information',
message: 'Image file size must not exceed 5MB',
});
this.resetFileInput();
return;
}
const fileNameElement = document.getElementById('fileName');
if (fileNameElement) {
fileNameElement.textContent = file.name;
}
// this.imageFile = file;
this.loading = true;
const reader = new FileReader();
@@ -319,10 +345,9 @@ export class DeckListComponent implements OnInit {
const xMax = Math.max(...xs);
const yMin = Math.min(...ys);
const yMax = Math.max(...ys);
boxes.push({ x1: xMin, x2: xMax, y1: yMin, y2: yMax });
boxes.push({ x1: xMin, x2: xMax, y1: yMin, y2: yMax, inserted: null, updated: null });
});
const deckImage: DeckImage = { name: imageName, id: imageId, boxes };
//this.imageUploaded.emit({ imageSrc, deckImage });
const deckImage: DeckImage = { name: imageName, bildid: imageId, boxes };
this.imageData = { imageSrc, deckImage };
this.resetFileInput();
@@ -333,6 +358,7 @@ export class DeckListComponent implements OnInit {
};
reader.readAsDataURL(file);
}
/**
* Resets the file input field so the same file can be selected again.
*/

View File

@@ -10,8 +10,7 @@ export interface Deck {
export interface DeckImage {
boxes: Box[];
name: string;
//bildid: string;
id: string;
bildid: string;
}
export interface Box {
@@ -26,12 +25,13 @@ export interface Box {
reps?: number;
lapses?: number;
isGraduated?: boolean;
inserted: string;
updated: string;
}
export interface BackendBox {
bildname: string;
deckid: number;
iconindex: number;
id: number;
x1: number;
x2: number;
@@ -76,15 +76,15 @@ export class DeckService {
const imageMap: { [key: string]: DeckImage } = {};
images.forEach(image => {
if (!imageMap[image.id]) {
imageMap[image.id] = {
if (!imageMap[image.bildid]) {
imageMap[image.bildid] = {
name: image.name,
id: image.id,
bildid: image.bildid,
boxes: [],
};
}
imageMap[image.id].boxes.push({
id: image.boxid,
imageMap[image.bildid].boxes.push({
id: image.id,
x1: image.x1,
x2: image.x2,
y1: image.y1,
@@ -95,6 +95,8 @@ export class DeckService {
reps: image.reps,
lapses: image.lapses,
isGraduated: image.isGraduated ? true : false,
inserted: image.inserted,
updated: image.updated,
});
});

View File

@@ -24,7 +24,7 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
@ViewChild('canvas') canvasElement!: ElementRef<HTMLCanvasElement>;
detectedText: string = '';
boxes: { x1: number; x2: number; y1: number; y2: number }[] = [];
boxes: { x1: number; x2: number; y1: number; y2: number; id: number; inserted: string; updated: string }[] = [];
canvas!: fabric.Canvas;
maxCanvasWidth: number = 0;
@@ -133,6 +133,7 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
hasControls: true,
hasBorders: true,
objectCaching: false,
data: { id: box.id, inserted: box.inserted, updated: box.updated },
});
rect.on('modified', () => {
@@ -155,7 +156,6 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
});
this.updateBoxCoordinates();
// this.detectedText = ocrResults.map(result => result.text).join('\n');
} catch (error) {
console.error('Error processing image:', error);
@@ -198,6 +198,9 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
x2: Math.round(x2),
y1: Math.round(y1),
y2: Math.round(y2),
id: rect.data?.id,
inserted: rect.data?.inserted,
updated: rect.data?.updated,
});
});
@@ -226,7 +229,6 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
hasBorders: true,
objectCaching: false,
});
rect.on('modified', () => {
this.updateBoxCoordinates();
});
@@ -256,7 +258,7 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
const data = {
deckname: this.deckName,
bildname: this.imageData.deckImage?.name, // this.imageFile?.name,
bildid: this.imageData.deckImage?.id,
bildid: this.imageData.deckImage?.bildid,
boxes: this.boxes,
};
this.deckService.saveImageData(data).subscribe({

View File

@@ -26,7 +26,7 @@ export class MoveImageModalComponent {
return;
}
this.deckService.moveImage(this.image.id, this.selectedDeckId).subscribe({
this.deckService.moveImage(this.image.bildid, this.selectedDeckId).subscribe({
next: () => {
this.moveCompleted.emit();
this.close();

View File

@@ -8,19 +8,22 @@ export class PopoverService {
title: string;
message: string;
showInput: boolean;
showCancel: boolean;
inputValue: string;
confirmText: string;
onConfirm: () => void;
onCancel?: () => void;
}>();
popoverState$ = this.showPopoverSource.asObservable();
show(options: { title: string; message: string; confirmText?: string; onConfirm?: (inputValue?: string) => void; onCancel?: () => void }) {
show(options: { title: string; message: string; confirmText?: string; showCancel?: boolean; onConfirm?: (inputValue?: string) => void; onCancel?: () => void }) {
this.showPopoverSource.next({
showInput: false,
inputValue: null,
confirmText: 'Ok',
showCancel: false,
onConfirm: (inputValue?: string) => {},
...options,
});
@@ -28,6 +31,7 @@ export class PopoverService {
showWithInput(options: { title: string; message: string; confirmText: string; inputValue: string; onConfirm: (inputValue?: string) => void; onCancel?: () => void }) {
this.showPopoverSource.next({
showInput: true,
showCancel: true,
...options,
});
}

View File

@@ -125,7 +125,7 @@ export class TrainingComponent implements OnInit {
if (!ctx || !this.currentImageData) return;
const img = new Image();
img.src = `/debug_images/${this.currentImageData.id}/original_compressed.jpg`;
img.src = `/debug_images/${this.currentImageData.bildid}/original_compressed.jpg`;
img.onload = () => {
// Set the canvas size to the image size
canvas.width = img.width;