responsive part 1, new orientation

This commit is contained in:
Andreas Knuth 2025-01-16 00:07:10 +00:00
parent 49c1185372
commit ddac7102fe
2 changed files with 107 additions and 86 deletions

View File

@ -1,78 +1,94 @@
<div>
<div class="flex flex-col">
<!-- Button to create a new deck -->
<div class="flex justify-end mb-4">
<!-- <div class="flex justify-end mb-4">
<button (click)="openCreateDeckModal()" class="bg-green-500 text-white py-2 px-4 rounded hover:bg-green-600">
Create New Deck
</button>
</div>
</div> -->
<!-- Display decks -->
<div class="flex flex-wrap">
<div *ngFor="let deck of decks" class="bg-white shadow rounded-lg p-6 w-full md:w-1/2 lg:w-1/3 flex flex-col border-dashed border-2 border-indigo-600">
<!-- Deck header with toggle button -->
<div class="flex justify-between items-center mb-4">
<div class="flex items-center space-x-2">
<h2 class="text-xl font-semibold">{{ deck.name }}</h2>
<span class="text-gray-600">({{ deck.images.length }} images)</span>
</div>
<div class="flex space-x-2">
<!-- Button zum Löschen -->
<button (click)="openDeletePopover(deck.name)" class="text-red-500 hover:text-red-700" title="Delete Deck">
<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="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<!-- Button zum Umbenennen -->
<button (click)="openRenamePopover(deck)" class="text-yellow-500 hover:text-yellow-700" title="Rename Deck">
<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>
<button (click)="toggleDeckExpansion(deck.name)" class="text-gray-500 hover:text-gray-700 focus:outline-none" title="Expand/Collapse Deck">
<svg *ngIf="!isDeckExpanded(deck.name)" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 transform rotate-0 transition-transform duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
<svg *ngIf="isDeckExpanded(deck.name)" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 transform rotate-180 transition-transform duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<!-- Two-column layout -->
<div class="flex flex-col md:flex-row gap-4 mx-auto max-w-5xl">
<!-- Left column: List of decks -->
<div class="w-full md:w-1/3">
<div class="bg-white shadow rounded-lg p-4">
<div class="flex">
<h2 class="text-xl font-semibold mb-4">Decks</h2>
<button (click)="openCreateDeckModal()" class="text-gray-500 hover:text-gray-700 focus:outline-none flex items-start ml-2.5 translate-y-0.5">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="#2563eb">
<circle cx="12" cy="12" r="9" stroke-width="2"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v8M8 12h8"/>
</svg>
</button>
</div>
<div class="space-y-2">
<div *ngFor="let deck of decks"
class="flex justify-between items-center p-2 rounded hover:bg-gray-100 cursor-pointer"
[class.bg-blue-100]="isDeckActive(deck)">
<div class="flex items-center space-x-2 whitespace-nowrap">
<h3 class="font-medium">{{ deck.name }}</h3>
<span class="text-gray-600">({{ deck.images.length }})</span>
</div>
<button (click)="toggleDeckExpansion(deck)" class="text-gray-500 hover:text-gray-700 focus:outline-none">
<svg *ngIf="!isDeckActive(deck)" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 transform rotate-0 transition-transform duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
<svg *ngIf="isDeckActive(deck)" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 transform rotate-180 transition-transform duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</button>
</div>
</div>
</div>
</div>
<!-- Image list and action buttons only shown if the deck is expanded and no training is active -->
<ng-container *ngIf="isDeckExpanded(deck.name) && !selectedDeck">
<!-- List of images with number of boxes and icons -->
<!-- Right column: Active deck details -->
<div class="w-full md:w-2/3" *ngIf="activeDeck">
<div class="bg-white shadow rounded-lg p-6 border-dashed border-2 border-indigo-600">
<!-- Deck header -->
<div class="flex justify-between items-center mb-4">
<div class="flex items-center space-x-2">
<h2 class="text-xl font-semibold">{{ activeDeck.name }}</h2>
<span class="text-gray-600">({{ activeDeck.images.length }} images)</span>
</div>
<div class="flex space-x-2">
<button (click)="openDeletePopover(activeDeck.name)" class="text-red-500 hover:text-red-700" title="Delete Deck">
<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="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<button (click)="openRenamePopover(activeDeck)" class="text-yellow-500 hover:text-yellow-700" title="Rename Deck">
<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>
<!-- Image list -->
<ul class="mb-4">
<li *ngFor="let image of deck.images" class="flex justify-between items-center py-2 border-b last:border-b-0">
<li *ngFor="let image of activeDeck.images" class="flex justify-between items-center py-2 border-b last:border-b-0">
<div class="flex items-center space-x-2">
<div class="relative group">
<!-- Tooltip content (image) -->
<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="/api/debug_image/{{image.id}}/thumbnail.jpg" alt="{{ image.name }}" class="w-full h-auto object-cover">
</div>
<!-- Image name with tooltip -->
<span class="font-medium cursor-pointer">
{{ image.name }}
</span>
<span class="font-medium cursor-pointer">{{ image.name }}</span>
</div>
<span class="text-gray-600">({{ image.boxes.length }} boxes)</span>
</div>
<div class="flex space-x-2">
<!-- Edit Icon -->
<button (click)="editImage(deck, image)" class="text-blue-500 hover:text-blue-700" title="Edit Image">
<button (click)="editImage(activeDeck, image)" class="text-blue-500 hover:text-blue-700" title="Edit 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="M11 4H4v7m0 0l9-9 9 9M20 13v7h-7m0 0l-9-9-9 9" />
</svg>
</button>
<!-- Delete Icon -->
<button (click)="deleteImage(deck, image)" class="text-red-500 hover:text-red-700" title="Delete Image">
<button (click)="deleteImage(activeDeck, image)" class="text-red-500 hover:text-red-700" title="Delete 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="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<!-- Move Icon -->
<button (click)="openMoveImageModal(deck, image)" class="text-yellow-500 hover:text-yellow-700" title="Move Image">
<button (click)="openMoveImageModal(activeDeck, 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="M5 13l4 4L19 7" />
</svg>
@ -82,18 +98,17 @@
</ul>
<!-- Action buttons -->
<div class="flex space-x-2">
<button (click)="openTraining(deck)" class="flex-1 bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600">
<div class="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2">
<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)="openUploadImageModal(deck.name)" class="flex-1 bg-green-500 text-white py-2 px-4 rounded hover:bg-green-600">
<button (click)="openUploadImageModal(activeDeck.name)" class="flex-1 bg-green-500 text-white py-2 px-4 rounded hover:bg-green-600">
Add Image
</button>
</div>
</ng-container>
</div>
</div>
</div>
<!-- CreateDeckModalComponent -->
<app-create-deck-modal (deckCreated)="loadDecks()"></app-create-deck-modal>
@ -110,7 +125,7 @@
[decks]="decks"
(moveCompleted)="onImageMoved()"
(closed)="imageToMove = null">
</app-move-image-modal>
</app-move-image-modal>
</div>
<!-- Popover für die Löschbestätigung -->
<div id="deletePopover" popover="manual" class="popover">

View File

@ -35,7 +35,7 @@ import { MoveImageModalComponent } from './move-image-modal/move-image-modal.com
export class DeckListComponent implements OnInit {
decks: Deck[] = [];
selectedDeck: Deck | null = null;
// Zustandsvariablen für die Popover-Interaktion
activeDeck: Deck | null = null;
private deckToDelete: string | null = null;
private deckToRename: Deck | null = null;
@ -63,10 +63,23 @@ export class DeckListComponent implements OnInit {
loadDecks(): void {
this.deckService.getDecks().subscribe({
next: (data) => this.decks = 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)
});
}
// Updated toggle method
toggleDeckExpansion(deck: Deck): void {
this.activeDeck = deck;
}
// Methode zum Öffnen des Lösch-Popovers
openDeletePopover(deckName: string): void {
this.deckToDelete = deckName;
@ -75,7 +88,10 @@ export class DeckListComponent implements OnInit {
deletePopover.showPopover();
}
}
// Method to check if a deck is active
isDeckActive(deck: Deck): boolean {
return this.activeDeck?.name === deck.name;
}
// Methode zum Bestätigen des Löschens
confirmDelete(): void {
if (this.deckToDelete) {
@ -142,25 +158,25 @@ export class DeckListComponent implements OnInit {
}
this.deckToRename = null;
}
// deleteDeck(deckName: string): void {
// if (!confirm(`Are you sure you want to delete the deck "${deckName}"?`)) {
// return;
// }
// this.deckService.deleteDeck(deckName).subscribe({
// next: () => this.loadDecks(),
// error: (err) => console.error('Error deleting deck', err)
// });
// }
deleteDeck(deckName: string): void {
if (!confirm(`Are you sure you want to delete the deck "${deckName}"?`)) {
return;
}
this.deckService.deleteDeck(deckName).subscribe({
next: () => this.loadDecks(),
error: (err) => console.error('Error deleting deck', err)
});
}
// 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)
// });
// }
// }
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)
});
}
}
deleteImage(deck: Deck, image: DeckImage): void {
if (!confirm(`Are you sure you want to delete the image "${image.name}"?`)) {
@ -217,16 +233,6 @@ export class DeckListComponent implements OnInit {
this.uploadImageModal.open();
}
// Method to toggle deck expansion
toggleDeckExpansion(deckName: string): void {
if (this.expandedDecks.has(deckName)) {
this.expandedDecks.delete(deckName);
} else {
this.expandedDecks.add(deckName);
}
this.saveExpandedDecks();
}
// Method to check if a deck is expanded
isDeckExpanded(deckName: string): boolean {
return this.expandedDecks.has(deckName);