parent
28393570a6
commit
576689a3ed
|
|
@ -124,6 +124,15 @@ export class DecksController {
|
||||||
return this.drizzleService.updateImage(data, user);
|
return this.drizzleService.updateImage(data, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Put('image/:bildid/rename')
|
||||||
|
async renameImage(@Request() req, @Param('bildid') bildid: string, @Body() data: { newImageName: string }) {
|
||||||
|
if (!data.newImageName) {
|
||||||
|
throw new HttpException('New image name is required', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
const user: User = req['user'];
|
||||||
|
return this.drizzleService.renameImage(bildid, data.newImageName, user);
|
||||||
|
}
|
||||||
|
|
||||||
@Delete('image/:bildid')
|
@Delete('image/:bildid')
|
||||||
async deleteImagesByBildId(@Request() req, @Param('bildid') bildid: string) {
|
async deleteImagesByBildId(@Request() req, @Param('bildid') bildid: string) {
|
||||||
const user: User = req['user'];
|
const user: User = req['user'];
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,32 @@ export class DrizzleService {
|
||||||
|
|
||||||
return { status: 'success', moved_entries: existingImages.length };
|
return { status: 'success', moved_entries: existingImages.length };
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Methode zum Umbenennen eines Decks
|
||||||
|
*/
|
||||||
|
async renameImage(bildId: string, newImagename: string, user: User) {
|
||||||
|
const existingImages = this.db
|
||||||
|
.select()
|
||||||
|
.from(Deck)
|
||||||
|
.where(and(eq(Deck.bildid, bildId), eq(Deck.user, user.email)));
|
||||||
|
if (existingImages.length === 0) {
|
||||||
|
throw new HttpException('Deck not found', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// const existingNewDeck = await this.getDeckByName(newDeckname, user);
|
||||||
|
// if (existingNewDeck.length > 0) {
|
||||||
|
// throw new HttpException('Deck with the new name already exists', HttpStatus.CONFLICT);
|
||||||
|
// }
|
||||||
|
|
||||||
|
await this.db
|
||||||
|
.update(Deck)
|
||||||
|
.set({
|
||||||
|
bildname: newImagename,
|
||||||
|
updated: sql`CURRENT_TIMESTAMP`, // Setze 'updated' auf CURRENT_TIMESTAMP
|
||||||
|
})
|
||||||
|
.where(and(eq(Deck.bildid, bildId), eq(Deck.user, user.email)));
|
||||||
|
return { status: 'success', message: 'Image Entries renamed successfully' };
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Methode zum Aktualisieren einer Box
|
* Methode zum Aktualisieren einer Box
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -9,25 +9,7 @@ import { PopoverService } from './services/popover.service';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
template: `
|
template: `
|
||||||
<!-- <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 *ngIf="!isLoggedIn" class="min-h-screen flex flex-col items-center justify-center" style="background: rgba(0, 119, 179, 0.1)">
|
||||||
<div class="text-center">
|
|
||||||
<h1 class="text-5xl font-bold mb-4">Master Your Learning</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="min-h-screen flex flex-col items-center justify-center" style="background: linear-gradient(to right, rgba(0, 119, 179, 0.95), rgba(255, 255, 255, 0.95));">
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="text-5xl font-bold mb-4">Master Your Learning</h1>
|
<h1 class="text-5xl font-bold mb-4">Master Your Learning</h1>
|
||||||
<p class="text-xl mb-8">Learn smarter, not harder. Start your journey today</p>
|
<p class="text-xl mb-8">Learn smarter, not harder. Start your journey today</p>
|
||||||
|
|
@ -45,24 +27,6 @@ import { PopoverService } from './services/popover.service';
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <div *ngIf="isLoggedIn" class="container mx-auto p-4">
|
|
||||||
<div class="flex justify-center items-center mb-8">
|
|
||||||
<h1 class="text-3xl font-bold mx-auto">Haiky Spaced Repetition Training</h1>
|
|
||||||
<div class="relative" appClickOutside (clickOutside)="showDropdown = false">
|
|
||||||
@if(photoURL){
|
|
||||||
<img [src]="photoURL" alt="User Photo" class="w-10 h-10 rounded-full cursor-pointer" (click)="toggleDropdown()" referrerpolicy="no-referrer" crossorigin="anonymous" />
|
|
||||||
} @else {
|
|
||||||
<div class="image-placeholder w-10 h-10 rounded-full cursor-pointer">Image</div>
|
|
||||||
} @if(showDropdown){
|
|
||||||
<div class="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg">
|
|
||||||
<button (click)="logout()" class="block w-full text-left px-4 py-2 text-gray-700 hover:bg-gray-100">Abmelden</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<app-deck-list></app-deck-list>
|
|
||||||
</div> -->
|
|
||||||
<div *ngIf="isLoggedIn" class="bg-white shadow mb-4">
|
<div *ngIf="isLoggedIn" class="bg-white shadow mb-4">
|
||||||
<div class="container mx-auto px-4 py-2 flex justify-between items-center">
|
<div class="container mx-auto px-4 py-2 flex justify-between items-center">
|
||||||
<!-- Logo und Name -->
|
<!-- Logo und Name -->
|
||||||
|
|
@ -73,11 +37,6 @@ import { PopoverService } from './services/popover.service';
|
||||||
|
|
||||||
<!-- Navigation -->
|
<!-- Navigation -->
|
||||||
<div class="hidden md:flex space-x-6">
|
<div class="hidden md:flex space-x-6">
|
||||||
<!-- <a href="#" class="text-gray-700 hover:text-gray-900">Kompakt</a>
|
|
||||||
<a href="#" class="text-gray-700 hover:text-gray-900">Umwandeln</a>
|
|
||||||
<a href="#" class="text-gray-700 hover:text-gray-900">Zusammenführen</a>
|
|
||||||
<a href="#" class="text-gray-700 hover:text-gray-900">Bearbeiten</a>
|
|
||||||
<a href="#" class="text-gray-700 hover:text-gray-900">KI PDF</a> -->
|
|
||||||
<span class="text-xl font-bold">Spaced Repetition Training</span>
|
<span class="text-xl font-bold">Spaced Repetition Training</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,11 @@
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<button (click)="openRenameImagePopover(image)" class="text-yellow-500 hover:text-yellow-700" title="Move Image">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -97,10 +102,24 @@
|
||||||
<button (click)="openTraining(activeDeck)" class="flex-1 bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600">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">
|
||||||
<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>
|
<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">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div>Add Image</div>
|
||||||
|
<div class="text-xs">(from file)</div>
|
||||||
|
</div>
|
||||||
|
</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" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Neuer Button Paste Image -->
|
||||||
|
<div class="flex-1">
|
||||||
|
<button (click)="pasteImage()" class="w-full bg-purple-500 text-white py-2 px-4 rounded hover:bg-purple-600">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div>Paste Image</div>
|
||||||
|
<div class="text-xs">(from clipboard)</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -66,11 +66,20 @@ export class DeckListComponent implements OnInit {
|
||||||
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
|
// Versuche, das zuvor gespeicherte aktive Deck zu laden
|
||||||
if (!this.activeDeck && this.decks.length > 0) {
|
const storedActiveDeckName = localStorage.getItem('activeDeckName');
|
||||||
|
if (storedActiveDeckName) {
|
||||||
|
const foundDeck = this.decks.find(deck => deck.name === storedActiveDeckName);
|
||||||
|
if (foundDeck) {
|
||||||
|
this.activeDeck = foundDeck;
|
||||||
|
} else if (this.decks.length > 0) {
|
||||||
this.activeDeck = this.decks[0];
|
this.activeDeck = this.decks[0];
|
||||||
|
localStorage.setItem('activeDeckName', this.activeDeck.name);
|
||||||
}
|
}
|
||||||
if (this.decks.length === 0) {
|
} else if (this.decks.length > 0) {
|
||||||
|
this.activeDeck = this.decks[0];
|
||||||
|
localStorage.setItem('activeDeckName', this.activeDeck.name);
|
||||||
|
} else {
|
||||||
this.activeDeck = null;
|
this.activeDeck = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -81,6 +90,7 @@ export class DeckListComponent implements OnInit {
|
||||||
// Updated toggle method
|
// Updated toggle method
|
||||||
toggleDeckExpansion(deck: Deck): void {
|
toggleDeckExpansion(deck: Deck): void {
|
||||||
this.activeDeck = deck;
|
this.activeDeck = deck;
|
||||||
|
localStorage.setItem('activeDeckName', deck.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to open the delete confirmation popover
|
// Method to open the delete confirmation popover
|
||||||
|
|
@ -138,6 +148,29 @@ export class DeckListComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openRenameImagePopover(image: DeckImage): void {
|
||||||
|
this.popoverService.showWithInput({
|
||||||
|
title: 'Rename Deck',
|
||||||
|
message: 'Enter the new name for the deck:',
|
||||||
|
confirmText: 'Rename',
|
||||||
|
inputValue: image.name,
|
||||||
|
onConfirm: (inputValue?: string) => this.confirmRenameImage(image, inputValue),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Method to confirm the renaming of a deck
|
||||||
|
confirmRenameImage(image: DeckImage, newName?: string): void {
|
||||||
|
if (newName && newName.trim() !== '' && newName !== image.name) {
|
||||||
|
this.deckService.renameImage(image.bildid, newName).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.loadDecks();
|
||||||
|
},
|
||||||
|
error: err => console.error('Error renaming image', err),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Optional: Handle ungültigen neuen Namen
|
||||||
|
console.warn('Invalid new image name.');
|
||||||
|
}
|
||||||
|
}
|
||||||
// Delete-Image Methoden ersetzen
|
// Delete-Image Methoden ersetzen
|
||||||
deleteImage(deck: Deck, image: DeckImage): void {
|
deleteImage(deck: Deck, image: DeckImage): void {
|
||||||
this.popoverService.show({
|
this.popoverService.show({
|
||||||
|
|
@ -368,6 +401,97 @@ export class DeckListComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liest das aktuelle Bild aus der Zwischenablage und setzt imageData, sodass die Edit-Image-Komponente
|
||||||
|
* mit dem eingefügten Bild startet.
|
||||||
|
*/
|
||||||
|
pasteImage(): void {
|
||||||
|
if (!navigator.clipboard || !navigator.clipboard.read) {
|
||||||
|
this.popoverService.show({
|
||||||
|
title: 'Fehler',
|
||||||
|
message: 'Das Clipboard-API wird in diesem Browser nicht unterstützt.',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.clipboard
|
||||||
|
.read()
|
||||||
|
.then(items => {
|
||||||
|
// Suche im Clipboard nach einem Element, das ein Bild enthält
|
||||||
|
for (const item of items) {
|
||||||
|
for (const type of item.types) {
|
||||||
|
if (type.startsWith('image/')) {
|
||||||
|
// Hole den Blob des Bildes
|
||||||
|
item.getType(type).then(blob => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
this.loading = true;
|
||||||
|
reader.onload = async e => {
|
||||||
|
const imageSrc = e.target?.result;
|
||||||
|
// Extrahiere den Base64-String (ähnlich wie in onFileChange)
|
||||||
|
const imageBase64 = (imageSrc as string).split(',')[1];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Optional: OCR-Request wie im File-Upload
|
||||||
|
const response = await this.http.post<any>('/api/ocr', { image: imageBase64 }).toPromise();
|
||||||
|
let deckImage: DeckImage;
|
||||||
|
if (response && response.results) {
|
||||||
|
const boxes: Box[] = [];
|
||||||
|
response.results.forEach((result: OcrResult) => {
|
||||||
|
const box = result.box;
|
||||||
|
const xs = box.map((point: number[]) => point[0]);
|
||||||
|
const ys = box.map((point: number[]) => point[1]);
|
||||||
|
const xMin = Math.min(...xs);
|
||||||
|
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, inserted: null, updated: null });
|
||||||
|
});
|
||||||
|
deckImage = {
|
||||||
|
name: 'Pasted Image',
|
||||||
|
bildid: response.results.length > 0 ? response.results[0].name : null,
|
||||||
|
boxes,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Falls kein OCR-Ergebnis vorliegt, lege leere Boxen an
|
||||||
|
deckImage = {
|
||||||
|
name: 'Pasted Image',
|
||||||
|
bildid: null,
|
||||||
|
boxes: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Setze imageData – dadurch wird in der Template der <app-edit-image-modal> eingeblendet
|
||||||
|
this.imageData = { imageSrc, deckImage };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error with OCR service:', error);
|
||||||
|
this.popoverService.show({
|
||||||
|
title: 'Error',
|
||||||
|
message: 'Error with OCR service.',
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
return; // Beende die Schleife, sobald ein Bild gefunden wurde.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Falls kein Bild gefunden wurde:
|
||||||
|
this.popoverService.show({
|
||||||
|
title: 'Information',
|
||||||
|
message: 'Keine Bilddaten im Clipboard gefunden.',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('Fehler beim Zugriff auf das Clipboard:', err);
|
||||||
|
this.popoverService.show({
|
||||||
|
title: 'Fehler',
|
||||||
|
message: 'Fehler beim Zugriff auf das Clipboard.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Methode zur Berechnung des nächsten Trainingsdatums
|
// Methode zur Berechnung des nächsten Trainingsdatums
|
||||||
getNextTrainingDate(deck: Deck): Date {
|
getNextTrainingDate(deck: Deck): Date {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,9 @@ export class DeckService {
|
||||||
renameDeck(oldDeckName: string, newDeckName: string): Observable<any> {
|
renameDeck(oldDeckName: string, newDeckName: string): Observable<any> {
|
||||||
return this.http.put(`${this.apiUrl}/${encodeURIComponent(oldDeckName)}/rename`, { newDeckName });
|
return this.http.put(`${this.apiUrl}/${encodeURIComponent(oldDeckName)}/rename`, { newDeckName });
|
||||||
}
|
}
|
||||||
|
renameImage(bildid: string, newImageName: string): Observable<any> {
|
||||||
|
return this.http.put(`${this.apiUrl}/image/${encodeURIComponent(bildid)}/rename`, { newImageName });
|
||||||
|
}
|
||||||
saveImageData(data: any): Observable<any> {
|
saveImageData(data: any): Observable<any> {
|
||||||
return this.http.post(`${this.apiUrl}/image`, data);
|
return this.http.post(`${this.apiUrl}/image`, data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,42 @@
|
||||||
<div #editImageModal id="editImageModal" tabindex="-1" aria-hidden="true" class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-modal md:h-full">
|
<div
|
||||||
|
#editImageModal
|
||||||
|
id="editImageModal"
|
||||||
|
data-modal-backdrop="static"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-hidden="true"
|
||||||
|
class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-modal md:h-full"
|
||||||
|
>
|
||||||
<div class="relative h-full contents">
|
<div class="relative h-full contents">
|
||||||
<div class="relative bg-white rounded-lg shadow">
|
<div class="relative bg-white rounded-lg shadow p-[20px]">
|
||||||
<button type="button" class="absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center" (click)="closeModal()">
|
|
||||||
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
|
||||||
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="sr-only">Close</span>
|
|
||||||
</button>
|
|
||||||
<div class="p-6 relative">
|
|
||||||
<!-- Header with box count -->
|
<!-- Header with box count -->
|
||||||
<h3 class="mb-4 text-xl font-medium text-gray-900">
|
<div class="flex items-center justify-between px-4 pb-4 border-b rounded-t dark:border-gray-600 border-gray-200">
|
||||||
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
|
||||||
Edit Image <span *ngIf="boxes.length > 0">({{ boxes.length }} Box{{ boxes.length > 1 ? 'es' : '' }})</span>
|
Edit Image <span *ngIf="boxes.length > 0">({{ boxes.length }} Box{{ boxes.length > 1 ? 'es' : '' }})</span>
|
||||||
</h3>
|
</h3>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
|
||||||
|
(click)="closeModal()"
|
||||||
|
>
|
||||||
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Close modal</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<!-- Canvas -->
|
<!-- Canvas -->
|
||||||
|
<div class="p-4 md:p-5 space-y-4">
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<canvas #canvas class="border border-gray-300 rounded w-full h-auto"></canvas>
|
<canvas #canvas class="border border-gray-300 rounded w-full h-auto"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Buttons below the canvas -->
|
<!-- Buttons below the canvas -->
|
||||||
<div class="mt-4 flex justify-between">
|
<div class="mt-4 flex justify-between">
|
||||||
<button (click)="save()" class="bg-green-500 text-white py-2 px-4 rounded hover:bg-green-600">
|
<button (click)="save()" class="bg-green-500 text-white py-2 px-4 rounded hover:bg-green-600">Save</button>
|
||||||
Save
|
<button (click)="addNewBox()" class="bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600">New Box</button>
|
||||||
</button>
|
|
||||||
<button (click)="addNewBox()" class="bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600">
|
|
||||||
New Box
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- </div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -37,6 +37,7 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
async ngAfterViewInit() {
|
async ngAfterViewInit() {
|
||||||
this.modal = new Modal(this.modalElement.nativeElement, {
|
this.modal = new Modal(this.modalElement.nativeElement, {
|
||||||
|
backdrop: 'static',
|
||||||
onHide: () => {
|
onHide: () => {
|
||||||
this.closed.emit();
|
this.closed.emit();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: linear-gradient(to bottom, #f8fafc, #fff7d6);
|
background: rgba(0, 119, 179, 0.1);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue