|
|
|
@ -1,73 +1,120 @@
|
|
|
|
|
<!-- Spinner pendant le chargement -->
|
|
|
|
|
<div *ngIf="loading" class="flex justify-center items-center h-64">
|
|
|
|
|
<div
|
|
|
|
|
class="animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"
|
|
|
|
|
></div>
|
|
|
|
|
<div class="animate-spin rounded-full h-16 w-16 border-4 border-blue-500 border-t-transparent"></div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Timeline -->
|
|
|
|
|
<div
|
|
|
|
|
*ngIf="!loading && pins.length > 0"
|
|
|
|
|
class="relative mx-auto max-w-3xl py-10 px-4"
|
|
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
class="absolute h-full left-1/2 transform -translate-x-1/2 border-l-2 border-blue-500"
|
|
|
|
|
></div>
|
|
|
|
|
<!-- Timeline principale -->
|
|
|
|
|
<div *ngIf="!loading && pins.length > 0" class="relative mx-auto max-w-7xl py-20 px-6">
|
|
|
|
|
<!-- Barre centrale -->
|
|
|
|
|
<div class="absolute left-1/2 transform -translate-x-1/2 h-full bg-blue-500 w-6 rounded-full z-0"></div>
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
*ngFor="let pin of pins; let i = index"
|
|
|
|
|
class="mb-12 flex justify-between items-center w-full"
|
|
|
|
|
>
|
|
|
|
|
<!-- Si pair, carte à droite, point à gauche -->
|
|
|
|
|
<div
|
|
|
|
|
class="w-5/12"
|
|
|
|
|
[ngClass]="{ 'order-1': i % 2 === 0, 'order-2': i % 2 !== 0 }"
|
|
|
|
|
></div>
|
|
|
|
|
<!-- Groupement par années -->
|
|
|
|
|
<ng-container *ngFor="let year of sortedYears">
|
|
|
|
|
<!-- Marqueur d'année -->
|
|
|
|
|
<div class="relative mb-24 flex justify-center items-center">
|
|
|
|
|
<div class="absolute left-1/2 transform -translate-x-1/2 h-16 w-8 bg-blue-500 z-0"></div>
|
|
|
|
|
<div class="bg-blue-600 text-white text-4xl font-bold px-10 py-5 rounded-full shadow-2xl z-10 border-4 border-white">
|
|
|
|
|
{{ year }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Pins de l'année -->
|
|
|
|
|
<ng-container *ngFor="let pin of groupedPins[year]; let i = index">
|
|
|
|
|
<div class="mb-32 flex flex-col sm:flex-row justify-between items-center w-full relative z-10">
|
|
|
|
|
<!-- Espace vide -->
|
|
|
|
|
<div class="w-full sm:w-5/12" [ngClass]="{ 'order-1': i % 2 === 0, 'order-2': i % 2 !== 0 }"></div>
|
|
|
|
|
|
|
|
|
|
<!-- Bulle centrale avec la date -->
|
|
|
|
|
<div
|
|
|
|
|
class="z-20 flex items-center bg-blue-600 shadow-lg w-10 h-10 rounded-full"
|
|
|
|
|
class="z-20 flex items-center justify-center bg-white border-[6px] border-blue-600 text-blue-700 font-bold text-lg shadow-2xl w-32 h-32 rounded-full text-center px-4 leading-tight"
|
|
|
|
|
[ngClass]="{ 'order-2': i % 2 === 0, 'order-1': i % 2 !== 0 }"
|
|
|
|
|
>
|
|
|
|
|
<span class="text-white font-semibold mx-auto">{{ i + 1 }}</span>
|
|
|
|
|
<span>{{ pin.date | date: 'dd/MM/yyyy' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Content card -->
|
|
|
|
|
<!-- Carte de contenu -->
|
|
|
|
|
<div
|
|
|
|
|
class="bg-white dark:bg-gray-800 rounded-xl shadow-xl px-6 py-5 w-5/12 transition-all duration-300 hover:scale-[1.02]"
|
|
|
|
|
class="bg-white dark:bg-gray-800 rounded-3xl shadow-2xl px-10 py-8 w-full sm:w-5/12 transition-all duration-300 hover:scale-[1.02]"
|
|
|
|
|
[ngClass]="{
|
|
|
|
|
'order-3': i % 2 === 0,
|
|
|
|
|
'order-0': i % 2 !== 0,
|
|
|
|
|
'text-right': i % 2 !== 0
|
|
|
|
|
'order-3 text-center sm:text-left': i % 2 === 0,
|
|
|
|
|
'order-0 text-center sm:text-right': i % 2 !== 0
|
|
|
|
|
}"
|
|
|
|
|
>
|
|
|
|
|
<div class="text-sm text-gray-400 mb-1">
|
|
|
|
|
{{ pin.date ? (pin.date | date : "dd/MM/yyyy") : "Date inconnue" }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-lg font-bold text-gray-900 dark:text-white mb-1">
|
|
|
|
|
{{ pin.title || "Titre inconnu" }}
|
|
|
|
|
<!-- Titre centré -->
|
|
|
|
|
<h3 class="text-3xl font-extrabold text-gray-900 dark:text-white mb-4 text-center">
|
|
|
|
|
{{ pin.title || 'Titre inconnu' }}
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<!-- Description justifiée tronquée -->
|
|
|
|
|
<div
|
|
|
|
|
class="text-lg text-gray-700 dark:text-gray-300 mb-4 text-justify transition-all duration-300"
|
|
|
|
|
[ngClass]="{
|
|
|
|
|
'line-clamp-5 overflow-hidden': !expandedDescriptions[pins.indexOf(pin)]
|
|
|
|
|
}"
|
|
|
|
|
>
|
|
|
|
|
{{ pin.description || 'Aucune description' }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-gray-700 dark:text-gray-300 mb-3">
|
|
|
|
|
{{ pin.description || "Aucune description" }}
|
|
|
|
|
|
|
|
|
|
<!-- Bouton "voir plus / moins" -->
|
|
|
|
|
<div *ngIf="pin.description.length > 200" class="text-right mb-6">
|
|
|
|
|
<button
|
|
|
|
|
class="text-blue-600 font-semibold hover:underline"
|
|
|
|
|
(click)="toggleDescription(pins.indexOf(pin))"
|
|
|
|
|
>
|
|
|
|
|
{{ expandedDescriptions[pins.indexOf(pin)] ? 'Voir moins' : 'Voir plus' }}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<ng-container *ngIf="imageUrls[i] && imageUrls[i].length > 0">
|
|
|
|
|
<!-- Carrousel d'images -->
|
|
|
|
|
<ng-container *ngIf="imageUrls[pins.indexOf(pin)].length > 0">
|
|
|
|
|
<div class="relative flex items-center justify-center">
|
|
|
|
|
<button
|
|
|
|
|
*ngIf="imageUrls[pins.indexOf(pin)].length > 1"
|
|
|
|
|
(click)="prevImage(pins.indexOf(pin))"
|
|
|
|
|
class="absolute left-0 bg-white border border-blue-500 text-blue-500 rounded-full w-10 h-10 shadow-md hover:bg-blue-100 z-10"
|
|
|
|
|
>
|
|
|
|
|
‹
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<img
|
|
|
|
|
[src]="imageUrls[i][0]"
|
|
|
|
|
[src]="imageUrls[pins.indexOf(pin)][carouselIndexes[pins.indexOf(pin)]]"
|
|
|
|
|
alt="image"
|
|
|
|
|
class="rounded-lg mx-auto max-h-40 object-cover shadow"
|
|
|
|
|
class="rounded-xl mx-auto w-full h-64 object-cover shadow-lg"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
*ngIf="imageUrls[pins.indexOf(pin)].length > 1"
|
|
|
|
|
(click)="nextImage(pins.indexOf(pin))"
|
|
|
|
|
class="absolute right-0 bg-white border border-blue-500 text-blue-500 rounded-full w-10 h-10 shadow-md hover:bg-blue-100 z-10"
|
|
|
|
|
>
|
|
|
|
|
›
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Indicateur de position -->
|
|
|
|
|
<div *ngIf="imageUrls[pins.indexOf(pin)].length > 1" class="flex justify-center mt-2 space-x-2">
|
|
|
|
|
<div
|
|
|
|
|
*ngFor="let img of imageUrls[pins.indexOf(pin)]; let j = index"
|
|
|
|
|
class="w-3 h-3 rounded-full"
|
|
|
|
|
[ngClass]="{
|
|
|
|
|
'bg-blue-600': j === carouselIndexes[pins.indexOf(pin)],
|
|
|
|
|
'bg-blue-200': j !== carouselIndexes[pins.indexOf(pin)]
|
|
|
|
|
}"
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
</ng-container>
|
|
|
|
|
<ng-container *ngIf="!imageUrls[i] || imageUrls[i].length === 0">
|
|
|
|
|
|
|
|
|
|
<!-- Fallback s’il n’y a pas d’image -->
|
|
|
|
|
<ng-container *ngIf="!imageUrls[pins.indexOf(pin)] || imageUrls[pins.indexOf(pin)].length === 0">
|
|
|
|
|
<div class="text-gray-400 italic text-center">Aucune image</div>
|
|
|
|
|
</ng-container>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</ng-container>
|
|
|
|
|
</ng-container>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Message si vide -->
|
|
|
|
|
<div
|
|
|
|
|
*ngIf="!loading && pins.length === 0"
|
|
|
|
|
class="text-center text-gray-500 py-12 text-lg"
|
|
|
|
|
>
|
|
|
|
|
<div *ngIf="!loading && pins.length === 0" class="text-center text-gray-500 py-12 text-xl">
|
|
|
|
|
Aucun souvenir à afficher pour le moment.
|
|
|
|
|
</div>
|
|
|
|
|