You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
8.6 KiB
241 lines
8.6 KiB
<!-- Spinner pendant le chargement -->
|
|
<div *ngIf="loading" class="flex justify-center items-center h-64">
|
|
<div
|
|
class="animate-spin rounded-full h-16 w-16 border-4 border-blue-500 border-t-transparent"
|
|
></div>
|
|
</div>
|
|
|
|
<!-- Timeline principale -->
|
|
<div
|
|
*ngIf="!loading && pins.length > 0"
|
|
class="relative mx-auto max-w-7xl py-20 px-6 z-0"
|
|
>
|
|
<!-- 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>
|
|
|
|
<!-- 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-2xl 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]="{ 'sm:order-1': i % 2 === 0, 'sm:order-2': i % 2 !== 0 }"
|
|
></div>
|
|
|
|
<!-- Bulle centrale avec la date -->
|
|
<div
|
|
class="z-20 flex items-center justify-center bg-white border-[6px] sm:border-blue-600 border-gray-800 sm:text-blue-700 text-gray-800 font-bold text-base sm:text-lg shadow-2xl sm:rounded-full rounded-t-3xl sm:w-32 sm:h-32 text-center leading-tight px-4 py-2 sm:px-10 sm:py-5 date-bubble"
|
|
[ngClass]="{
|
|
'sm:order-2': i % 2 === 0,
|
|
'sm:order-1': i % 2 !== 0
|
|
}"
|
|
>
|
|
<span>{{ pin.date | date : "d MMMM yyyy" }}</span>
|
|
</div>
|
|
|
|
<!-- Ligne de liaison (desktop uniquement) -->
|
|
<div
|
|
class="hidden -z-10 sm:block absolute top-1/2 transform -translate-y-1/2 h-2 w-[calc(50%-8rem)] bg-blue-500"
|
|
[ngClass]="{
|
|
'left-1/2': i % 2 === 0,
|
|
'right-1/2': i % 2 !== 0
|
|
}"
|
|
></div>
|
|
|
|
<!-- Carte de contenu -->
|
|
<div
|
|
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] cursor-pointer"
|
|
[ngClass]="{
|
|
'sm:order-3 sm:text-left text-center': i % 2 === 0,
|
|
'sm:order-0 sm:text-right text-left': i % 2 !== 0
|
|
}"
|
|
(click)="navigateToPinOnMap(pin.id)"
|
|
>
|
|
<!-- Titre centré -->
|
|
<h3
|
|
class="text-2xl 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-md 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>
|
|
|
|
<!-- 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>
|
|
|
|
<!-- Carrousel d'images -->
|
|
<ng-container *ngIf="imageUrls[pins.indexOf(pin)].length > 0">
|
|
<div
|
|
class="relative h-64 mt-2 overflow-hidden rounded-lg flex items-center justify-center"
|
|
>
|
|
<div
|
|
*ngFor="
|
|
let imageId of imageUrls[pins.indexOf(pin)];
|
|
let index = index
|
|
"
|
|
[class]="
|
|
'absolute inset-0 transition-opacity duration-700 ease-in-out' +
|
|
(index === carouselIndexes[pins.indexOf(pin)]
|
|
? ' opacity-100'
|
|
: ' opacity-0')
|
|
"
|
|
>
|
|
<img
|
|
[src]="imageId"
|
|
class="object-contain max-h-full max-w-full h-full w-auto mx-auto"
|
|
alt="image"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Slider controls -->
|
|
<div *ngIf="imageUrls[pins.indexOf(pin)].length > 1">
|
|
<button
|
|
type="button"
|
|
class="absolute top-0 left-0 z-30 flex items-center justify-center h-full cursor-pointer group focus:outline-none"
|
|
(click)="
|
|
prevImage(pins.indexOf(pin)); $event.stopPropagation()
|
|
"
|
|
>
|
|
<span
|
|
class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-black/30 group-hover:bg-black/50"
|
|
>
|
|
<svg
|
|
class="w-4 h-4 text-white"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
fill="none"
|
|
viewBox="0 0 6 10"
|
|
>
|
|
<path
|
|
stroke="currentColor"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M5 1 1 5l4 4"
|
|
/>
|
|
</svg>
|
|
<span class="sr-only">Précédent</span>
|
|
</span>
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
class="absolute top-0 right-0 z-30 flex items-center justify-center h-full cursor-pointer group focus:outline-none"
|
|
(click)="
|
|
nextImage(pins.indexOf(pin)); $event.stopPropagation()
|
|
"
|
|
>
|
|
<span
|
|
class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-black/30 group-hover:bg-black/50"
|
|
>
|
|
<svg
|
|
class="w-4 h-4 text-white"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
fill="none"
|
|
viewBox="0 0 6 10"
|
|
>
|
|
<path
|
|
stroke="currentColor"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M1 9l4-4-4-4"
|
|
/>
|
|
</svg>
|
|
<span class="sr-only">Suivant</span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</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>
|
|
|
|
<!-- 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>
|
|
|
|
<!-- Message si vide -->
|
|
<div
|
|
*ngIf="!loading && pins.length === 0"
|
|
class="text-center text-gray-500 py-12 text-xl"
|
|
>
|
|
Aucun souvenir à afficher pour le moment.
|
|
</div>
|
|
</div></ng-container
|
|
></ng-container
|
|
>
|
|
</div>
|
|
|
|
<div
|
|
*ngIf="!loading && pins.length === 0"
|
|
class="flex flex-col items-center justify-center h-64 space-y-6"
|
|
>
|
|
<p class="text-xl text-gray-800 text-center">
|
|
Commencez à créer votre histoire en ajoutant des souvenirs sur la carte !
|
|
</p>
|
|
<button
|
|
(click)="openPinModal()"
|
|
class="px-6 py-3 bg-gray-800 text-white rounded-lg hover:bg-gray-700 transition-colors duration-200 shadow-lg"
|
|
>
|
|
Ajouter un souvenir
|
|
</button>
|
|
</div>
|