Correction d'un test unitaire
Some checks failed
E2E Tests / e2e (push) Failing after 3m43s

Ajout d'un champs image dans les templates par défaut en + du champs nom, description pour avoir un exemple
Correction du visuel du champs d'ajout lors de la modification d'un template (apparition ligne pleine au lieu de texte en pointillé)
Ajout d'un intercepteur pour la partie démo de l'application afin de bien rafraichir le cache angular lorsque le temps de démo est expiré
This commit is contained in:
2026-04-25 09:23:56 +02:00
parent 88278bd1dd
commit 0582690dca
6 changed files with 116 additions and 17 deletions

View File

@@ -0,0 +1,65 @@
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';
/**
* Detecte la perte de session demo (orchestrateur) via les codes 401/502 sur
* les appels /api/*, affiche un overlay puis force un rechargement de la page.
* Le reload renvoie l'utilisateur sur la page "Preparation" pour creer une
* nouvelle session sans qu'il ait a faire Ctrl+Shift+R.
*
* Cet interceptor est inerte en mode normal (non-demo) : si le backend natif
* renvoie un 401 legitime, ca declenche aussi le reload, ce qui est sans
* consequence puisqu'aucun flux d'auth utilisateur n'existe encore cote app.
*/
// Module-level flag : evite de declencher overlay + reload plusieurs fois si
// plusieurs appels echouent en parallele juste apres l'expiration.
let alreadyTriggered = false;
export const sessionExpiredInterceptor: HttpInterceptorFn = (req, next) => {
return next(req).pipe(
catchError((err) => {
const isApiCall = req.url.includes('/api/');
const isSessionLoss =
err instanceof HttpErrorResponse && (err.status === 401 || err.status === 502);
if (isApiCall && isSessionLoss && !alreadyTriggered) {
alreadyTriggered = true;
showExpiredOverlay();
setTimeout(() => window.location.reload(), 2500);
}
return throwError(() => err);
})
);
};
function showExpiredOverlay(): void {
const overlay = document.createElement('div');
overlay.setAttribute('data-session-expired', 'true');
overlay.style.cssText = [
'position:fixed', 'inset:0',
'background:rgba(26,22,37,0.96)',
'color:#e4def5',
'display:flex', 'flex-direction:column',
'align-items:center', 'justify-content:center',
'gap:1rem',
'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',
'z-index:99999',
'text-align:center', 'padding:2rem',
].join(';');
overlay.innerHTML = `
<div style="font-size:1.5rem;color:#b794f4;">✦ Votre session démo a expiré</div>
<div style="color:#aaa0c5;max-width:420px;line-height:1.5;">
Une nouvelle session va être préparée automatiquement.<br>
Vos données précédentes ne sont pas conservées.
</div>
<div style="
width:32px;height:32px;margin-top:0.5rem;
border:3px solid rgba(183,148,244,0.2);
border-top-color:#b794f4;border-radius:50%;
animation:sex-spin 1s linear infinite;
"></div>
<style>@keyframes sex-spin{to{transform:rotate(360deg)}}</style>
`;
document.body.appendChild(overlay);
}

View File

@@ -42,7 +42,8 @@ export class TemplateCreateComponent implements OnInit, OnDestroy {
*/
fields: TemplateField[] = [
{ name: 'Nom', type: 'TEXT' },
{ name: 'Description', type: 'TEXT' }
{ name: 'Description', type: 'TEXT' },
{ name: 'Illustration', type: 'IMAGE', layout: 'GALLERY' }
];
/** Valeur courante de l'input d'ajout de champ (non binding direct pour reset facile). */
newFieldName = '';

View File

@@ -103,7 +103,7 @@
<option value="TEXT">Texte</option>
<option value="IMAGE">Image</option>
</select>
<button type="button" class="btn-add" (click)="addField()">
<button type="button" class="btn-add" (click)="addField()" title="Ajouter le champ">
<lucide-icon [img]="Plus" [size]="14"></lucide-icon>
</button>
</div>

View File

@@ -198,18 +198,7 @@
&::placeholder { color: #6b7280; }
}
&.add-row {
margin-top: 0.25rem;
border: 1px dashed #2a2a3d;
border-radius: 6px;
padding: 0;
input {
border: none;
background: transparent;
&:focus { border: none; }
}
}
&.add-row { margin-top: 0.5rem; }
.reorder-stack {
display: flex;

View File

@@ -2,9 +2,10 @@ import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { PreloadAllModules, provideRouter, withPreloading } from '@angular/router';
import { routes } from './app/app.routes';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { APP_INITIALIZER } from '@angular/core';
import { ConfigService } from './app/services/config.service';
import { sessionExpiredInterceptor } from './app/interceptors/session-expired.interceptor';
// withPreloading(PreloadAllModules) : une fois l'app initiale rendue, Angular
// telecharge en arriere-plan tous les chunks lazy-loades. Consequence : la
@@ -14,7 +15,7 @@ import { ConfigService } from './app/services/config.service';
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes, withPreloading(PreloadAllModules)),
provideHttpClient(),
provideHttpClient(withInterceptors([sessionExpiredInterceptor])),
{
provide: APP_INITIALIZER,
useFactory: (config: ConfigService) => () => config.load(),