Skip to content

refactor(cache): complete ziflux SWR migration and simplify stores#373

Open
neogenz wants to merge 26 commits intopreviewfrom
maximedesogus/pul-93-integrer-la-librairie-ziflux-pour-le-cache-swr-des-resources
Open

refactor(cache): complete ziflux SWR migration and simplify stores#373
neogenz wants to merge 26 commits intopreviewfrom
maximedesogus/pul-93-integrer-la-librairie-ziflux-pour-le-cache-swr-des-resources

Conversation

@neogenz
Copy link
Owner

@neogenz neogenz commented Mar 14, 2026

Summary

• Completes ziflux SWR cache integration across budget and template stores
• Removes custom cache infrastructure (DataCache, budget-invalidation service)
• Simplifies budget-details store with ziflux signal management
• Refactors template stores to use ziflux cachedResource patterns
• Consolidates budget calculations into shared BudgetFormulas utility

Changes

  • Cache Layer: Replace custom cache implementations with ziflux cachedResource/cachedMutation
  • Store Patterns: Unify store implementations around ziflux Signal-based reactive model
  • API Layer: Centralize budget sorting and caching logic at API boundary
  • Tests: Update store tests to reflect simplified ziflux patterns
  • Removed: 1900+ lines of custom cache code (DataCache, calculator services)

Type

refactor

@vercel
Copy link

vercel bot commented Mar 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pulpe-frontend Ready Ready Preview, Comment Mar 18, 2026 9:04pm
pulpe-landing Ready Ready Preview, Comment Mar 18, 2026 9:04pm

@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@supabase
Copy link

supabase bot commented Mar 14, 2026

This pull request has been ignored for the connected project qhhlloqisgzwcsrbdppn because there are no changes detected in backend-nest/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@claude
Copy link
Contributor

claude bot commented Mar 14, 2026

Review PR 373 - ziflux SWR migration. 0 bloquant - 2 importants - 1 suggestion. Verdict: CHANGES REQUESTED. Details dans les commentaires inline.

@claude
Copy link
Contributor

claude bot commented Mar 14, 2026

Review PR 373

Résumé : 0 bloquant(s) | 1 important(s) | 2 suggestion(s)

Important : budget-list-store.ts:L92 — Régression UX sur selectedYear

La nouvelle computation ne vérifie plus si l'année retenue existe dans plannedYears. Au premier chargement (pas de previous), selectedYear() retourne new Date().getFullYear() même si aucun budget n'existe pour l'année courante. Le composant s'en sort via Math.max(0, findIndex(...)) = 0, mais le signal diverge de l'onglet affiché jusqu'à la prochaine interaction utilisateur. (confiance: 90%)

Suggestion 1 : dashboard-store.ts:L342

return data! masque un null derrière un cast TypeScript sans bénéfice runtime. L'ancienne forme return data était plus honnête avec le compilateur. (confiance: 85%)

Suggestion 2 : budget-templates-api.ts

getDetail() fait 2 requêtes parallèles en cache miss (getById + getTemplateTransactions). Pattern hérité, désormais visible en core. Pas bloquant. (confiance: 82%)

Points positifs

Suppression de 1900 lignes de code maison (DataCache, BudgetInvalidationService, TemplateTotalsCalculator). Migration cachedResource / cachedMutation cohérente dans tous les stores. BudgetTemplatesApi déplacé correctement en core (partagé entre plusieurs features, providedIn root). deleteTemplate avec cachedMutation et rollback optimiste bien implémenté. Nouveau calculateTotalExpenseOnly dans shared/budget-formulas.ts réutilisable iOS et Angular. Internationalisation des chaînes hardcodées dans budget-list-page.ts. lines.asReadonly() dans TemplateLineStore : bonne encapsulation.

Verdict : APPROVE — La régression selectedYear est mineure (pas de crash, auto-récupération) mais mérite un correctif simple.

@github-actions
Copy link

github-actions bot commented Mar 14, 2026

🎭 Playwright E2E Tests

162 tests  ±0   162 ✅ ±0   8m 42s ⏱️ -4s
 36 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit ca94194. ± Comparison against base commit f91f88a.

♻️ This comment has been updated with latest results.

@claude
Copy link
Contributor

claude bot commented Mar 14, 2026

test review comment

@claude
Copy link
Contributor

claude bot commented Mar 14, 2026

Review PR 373

Resume : 0 bloquant - 1 important - 2 suggestions

IMPORTANT

budget-templates-api.ts L61-69 - createFromOnboarding n invalide pas le cache (confiance 95 pct)

Toutes les autres mutations de cette PR ajoutent .pipe(tap invalidate templates) : create, update, updateTemplateLines, bulkOperationsTemplateLines, delete. La methode createFromOnboarding a ete oubliee. Apres onboarding, le cache de la liste des templates reste perime jusqu a 5 min et le template fraichement cree peut ne pas apparaitre dans l UI.

SUGGESTIONS

  1. dashboard-store.ts - Regression optimistic vers pessimistic (confiance 85 pct)
    Le renommage de performOptimisticMutation en performMutationWithRefresh cache un changement de comportement : l UI ne se met plus a jour avant la reponse serveur. L utilisateur percoit un delai visible lors de l ajout de transaction. Si intentionnel, un commentaire l expliciterait.

  2. budget-list-store.ts L89-93 - selectedYear peut pointer une annee absente de plannedYears (confiance 85 pct)
    L ancienne computation retombait sur years[0] si l annee disparaissait. La nouvelle ignore le parametre years. Le composant compense via Math.max(0, findIndex) mais selectedYear perd sa garantie de coherence.

POINTS POSITIFS

Migration ngx-ziflux coherente sur tous les stores.
Suppression de ~1900 lignes custom (DataCache, BudgetInvalidationService, TemplateTotalsCalculator).
Consolidation dans BudgetFormulas (calculateTotalExpenseOnly) conforme au DRY.
Tests mis a jour de facon coherente.
Strings hardcodees migrees vers transloco.
BudgetTemplatesApi deplace de feature/ vers core/ - architecturalement correct.

Verdict : CHANGES REQUESTED (1 important a corriger)

@claude
Copy link
Contributor

claude bot commented Mar 14, 2026

Review PR #373 — refactor(cache): complete ziflux SWR migration

Résumé : 0 bloquant(s) — 2 important(s) — 2 suggestion(s)


Importants

  • feature/budget-templates/details/template-detail.ts:L532État d'erreur obsolète sur deleteTemplate.error() (confiance: 90%)

    Après un await this.#budgetTemplatesStore.deleteTemplate.mutate(templateId) réussi, la vérification deleteTemplate.error() peut retourner l'erreur d'une suppression précédente échouée (si cachedMutation ne réinitialise pas error() entre deux appels). Impact : fausse alerte snackbar d'erreur + navigateBack() jamais appelé alors que la suppression a réussi. Scénario : supprimer A (erreur réseau) → supprimer B (succès) → l'erreur de A est toujours visible.

  • core/budget-template/budget-templates-api.tscreateFromOnboarding$ n'invalide pas le cache (confiance: 95%)

    Toutes les mutations ont été migrées avec .pipe(tap(() => this.cache.invalidate(['templates']))) sauf createFromOnboarding$. Après la création d'un template depuis le flux d'onboarding, le cache ['templates', 'list'] reste périmé. La liste des templates dans BudgetTemplatesStore ne reflète pas la nouvelle entrée jusqu'à expiration naturelle (5 min).


Suggestions

  • feature/budget-templates/services/budget-templates-store.ts:L94-L103 — Double vérification if (response.data.template) consécutive dans addTemplate. Les deux blocs peuvent être fusionnés en un seul (voir commentaire inline).

  • feature/budget-templates/details/services/template-details-store.ts — La logique de entries, totals et netBalance est recalculée dans template-detail.spec.ts pour alimenter mockStore, ce qui teste la copie locale plutôt que le store lui-même. Voir commentaire inline.


Points positifs

  • La suppression de ~1 900 lignes de code custom (DataCache, BudgetInvalidationService) est un vrai gain de maintenabilité.
  • Migration cohérente vers cachedResource/cachedMutation de ngx-ziflux dans tous les stores impactés.
  • L'ajout de LoadingIndicator sur BudgetDetailsPage avec cleanup dans DestroyRef est correct.
  • Le déplacement de BudgetTemplatesApi de feature/ vers core/ est architecturalement justifié (utilisé par plusieurs features et par AuthCleanupService).
  • Les tests de TemplateDetailsStore et BudgetTemplatesStore couvrent bien les nouveaux patterns ziflux.
  • La conversion de deleteTemplate en cachedMutation simplifie le code tout en conservant le rollback optimiste.

Verdict : CHANGES REQUESTED — les 2 points importants méritent correction avant merge.

expect(mockBudgetInvalidation.invalidate).toHaveBeenCalledOnce();
expect(mockUserSettingsApi.reload).toHaveBeenCalledOnce();
expect(mockBudgetApi.cache.invalidate).toHaveBeenCalledOnce();
expect(mockUserSettingsStore.reload).toHaveBeenCalledOnce();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION] Assertion manquante sur mockBudgetTemplatesApi.cache.invalidate (confiance: 95%)

L'implémentation appelle this.#budgetTemplatesApi.cache.invalidate(['templates']) lors de chaque soft recovery, mais aucun des 4 scénarios "should trigger soft recovery" n'asserte ce comportement. Une régression (suppression de l'invalidation templates) passerait silencieusement.

Suggested change
expect(mockUserSettingsStore.reload).toHaveBeenCalledOnce();
expect(mockBudgetApi.cache.invalidate).toHaveBeenCalledOnce();
expect(mockBudgetTemplatesApi.cache.invalidate).toHaveBeenCalledOnce();
expect(mockUserSettingsStore.reload).toHaveBeenCalledOnce();

createBudget$: vi.fn().mockReturnValue(
of({
budget: { id: 'new-budget', month: 3, year: 2026 },
message: 'Budget créé',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION] Propriété message obsolète dans le mock (confiance: 90%)

CreateBudgetApiResponse.message a été supprimée de l'interface dans cette PR, mais le mock conserve message: 'Budget créé'. Ce n'est pas un bug (la propriété supplémentaire est ignorée), mais cela trompe les lecteurs futurs qui pourraient croire que la réponse inclut toujours un message.

Suggested change
message: 'Budget créé',
of({
budget: { id: 'new-budget', month: 3, year: 2026 },
}),

@@ -93,7 +81,7 @@ export class PreloadService {
// Transform to BudgetDetailsViewModel so the cache entry matches
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION] Le préchargement de ['budget', 'details', ...] ne bénéficie plus au DashboardStore (confiance: 85%)

Dans l'ancien code, getDashboardData$ faisait une lecture en cascade : il vérifiait ['budget', 'list'] puis ['budget', 'details', budgetId] avant de faire des appels HTTP. Désormais, getDashboardData$ est pure (0 lecture de cache) et DashboardStore cache le résultat final sous ['budget', 'dashboard', month, year].

Résultat : le premier chargement du dashboard génère toujours 2 appels HTTP (/budgets + /budgets/:id/details) même si le PreloadService vient de les pré-charger. Le prefetch de ['budget', 'details', budgetId] ne sert plus qu'au BudgetDetailsStore.

Pour restaurer le bénéfice du préchargement pour le dashboard, le PreloadService devrait aussi prefetch ['budget', 'dashboard', paddedMonth, year] via getDashboardData$(month, year).

throw error;
}
},
readonly budgetTemplates = cachedResource({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Important] budgetTemplates expose un CachedResourceRef mutable directement en propriété publique (confiance: 92%)

CachedResourceRef expose des méthodes mutables (.set(), .update(), .reload()) que n'importe quel composant peut appeler. template-list-page.ts y accède directement via store.budgetTemplates.status(), store.budgetTemplates.value(), etc. — ce qui viole l'encapsulation du store pattern défini dans angular-store-pattern.md.

Le pattern établi dans le projet (cf. BudgetListStore.budgets, BudgetDetailsStore.#budgetDetailsResource) est de passer le resource en privé (#) et d'exposer uniquement des computed() en lecture seule :

Suggested change
readonly budgetTemplates = cachedResource({
readonly #budgetTemplates = cachedResource({

Puis exposer des selectors :

readonly templates = computed(() => this.#budgetTemplates.value() ?? []);
readonly isLoading = this.#budgetTemplates.isInitialLoading;
readonly error = this.#budgetTemplates.error;
readonly status = computed(() => this.#budgetTemplates.status());

@claude
Copy link
Contributor

claude bot commented Mar 18, 2026

Review PR 373 - Migration ngx-ziflux

Resume : 0 bloquant - 0 important - 3 suggestions

Suggestions

  • page-lifecycle-recovery.service.spec.ts:110 - Assertion manquante sur mockBudgetTemplatesApi.cache.invalidate dans les 4 scenarios de soft recovery (confiance: 95%)
  • template-store.spec.ts:91 - Propriete message obsolete dans le mock de createBudget$ (confiance: 90%)
  • preload.service.ts:81 - Le prefetch de budget/details ne beneficie plus au DashboardStore (cache sous budget/dashboard) - premier chargement dashboard = 2 appels HTTP non evitables (confiance: 85%)

Points positifs

  • Migration ngx-ziflux propre : suppression de DataCache, BudgetInvalidationService et HasBudgetCache home-made
  • Derivation de getCachedBudgetExists() depuis le cache liste elegante : une seule source de verite
  • Logique hasBudgetGuard bien pensee : fresh=confiance totale, stale+true=confiance, stale+false=revalidation API
  • BudgetTemplatesApi promu vers core/ conforme aux regles architecture
  • Separation UserSettingsApi / UserSettingsStore claire et idiomatique
  • Tous les stores migrent vers cachedMutation avec invalidateKeys explicites
  • E2E mocks mis a jour pour la route /exists

Verdict : APPROVE

this.#api.cache.clear();
}

async #loadSettings(): Promise<UserSettings> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Important] Le catch silencieux neutralise le signal error et masque les échecs réseau aux utilisateurs (confiance: 88%)

Le try/catch intercepte toutes les erreurs et retourne { payDayOfMonth: null } — ce qui fait que la resource voit une résolution réussie. En conséquence :

  1. this.#settingsResource.error() ne sera jamais populé, même lors d'un échec 500 ou réseau.
  2. La propriété publique readonly error = this.#settingsResource.error est du dead code — aucun consommateur ne verra jamais d'erreur.
  3. La settings-page.ts ne peut pas afficher d'état d'erreur pour les settings.

Si l'intention est une dégradation silencieuse (soft failure acceptable), le signal error doit être supprimé et la property settings doit gérer null explicitement. Si une erreur doit être remontée, il faut laisser rethrow et laisser la resource gérer l'état d'erreur :

Suggested change
async #loadSettings(): Promise<UserSettings> {
async #loadSettings(): Promise<UserSettings> {
const response = await firstValueFrom(this.#api.getSettings$());
return response.data;
}


// Sort to put default template first
return [...viewModels].sort((a, b) => {
return viewModels.toSorted((a, b) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] La logique de tri duplique celle déjà présente dans TemplateStore.sortedTemplates (confiance: 83%)

TemplateStore.sortedTemplates (lignes 57-64 du store) trie déjà par isDefault desc puis createdAt desc. Ici, templateViewModels retrie par isDefault uniquement, ce qui peut diverger si le tri du store évolue. Il suffirait de construire les view models à partir de this.templateStore.sortedTemplates() plutôt que de templates() pour éliminer la duplication.

@claude
Copy link
Contributor

claude bot commented Mar 18, 2026

Review PR 373 - Resume: 0 bloquant - 2 importants - 1 suggestion. Voir commentaires inline pour details. Verdict: CHANGES REQUESTED

@claude
Copy link
Contributor

claude bot commented Mar 18, 2026

Review complete - Details

Verdict : CHANGES REQUESTED

0 bloquant - 2 importants - 1 suggestion

Importants :

  1. budget-templates-store.ts:18 - Encapsulation cassee : budgetTemplates est public (CachedResourceRef expose .set()/.update()). Tout le projet (BudgetListStore, BudgetDetailsStore) rend la resource privee et expose uniquement des computed(). template-list-page.ts appelle directement store.budgetTemplates.status() / .value() / .isLoading(). (confiance 92%)

  2. user-settings-store.ts:64 - Signal error inoperant : le try/catch dans #loadSettings retourne { payDayOfMonth: null } sur erreur API au lieu de rethrow. La resource ne peut jamais passer en etat erreur. readonly error = this.#settingsResource.error sera toujours undefined, rendant la detection d erreur impossible cote UI. (confiance 88%)

Suggestion :

  1. budget-creation-dialog.ts:224 - templateViewModels retrie par isDefault alors que TemplateStore.sortedTemplates() fournit deja ce tri. Risque de divergence si le tri evolue dans le store. (confiance 83%)

Points positifs : suppression de ~1900 lignes custom (DataCache, BudgetInvalidationService, TemplateTotalsCalculator), cachedMutation pattern uniforme dans BudgetDetailsStore, hasBudgetGuard simplifie, PreloadService reduit grace a cache.prefetch().

…expose errors

Remove error-swallowing try/catch that prevented error() signal from working. Replace
private #loadSettings() method with direct Observable loader using map() operator,
matching the pattern used by other 6 stores (BudgetListStore, TemplateStore, etc).

This restores proper SWR lifecycle where errors propagate to cachedResource.error()
while stale data is preserved in value() — the fallback { payDayOfMonth: null } was
redundant and masked failures.

Add UserSettingsStore spec with error propagation tests to validate the fix.
error instanceof Error
? error.message
: 'Une erreur est survenue lors de la sauvegarde';
: this.#transloco.translate('template.saveError');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[IMPORTANT] catch mort — mutate() ne rejette jamais (confiance: 85%)

Selon la doc ziflux intégrée dans cette PR : mutate() never rejects — les erreurs vont dans le signal error. Ce bloc catch ne peut donc jamais s'exécuter. La logique est déjà correctement gérée par le if (!response) aux lignes 154-161 via this.#bulkSaveMutation.error().

Le catch crée une fausse impression de protection et duplique le code d'erreur inutilement.

Fix : Supprimer le try/catch et conserver un try/finally nu pour le reset de isLoading :

async saveChanges(...): Promise<SaveResult> {
  // ...
  this.#isLoading.set(true);
  this.#error.set(null);

  try {
    const response = await this.#bulkSaveMutation.mutate({ templateId, operations });

    if (!response) { /* handle via mutation.error() */ }
    // ...
  } finally {
    this.#isLoading.set(false);
  }
}

@claude
Copy link
Contributor

claude bot commented Mar 18, 2026

Review PR #373

Résumé : 0 bloquant(s) · 1 important · 0 suggestion

Importants

  • template-line-store.ts:177 — Bloc catch mort : mutate() ne rejette jamais selon la doc ziflux intégrée dans cette PR. L'erreur est déjà gérée par le if (!response) via mutation.error(). Le catch duplique la logique et crée une fausse impression de protection. (confiance: 85%)

Points positifs

  • Migration complète et cohérente — La suppression de 1900+ lignes de code custom (DataCache, BudgetInvalidationService, HasBudgetCache, TemplateTotalsCalculator, template-api.ts) est une simplification réelle et mesurable. L'adoption de cachedResource / cachedMutation est homogène sur tous les stores.
  • Déplacement de BudgetTemplatesApi vers core/ — Correct architecturalement : l'API était dans feature/ mais est maintenant partagée entre plusieurs features. Le move vers core/ avec providedIn: 'root' suit la règle "Extract when 2+ features share it".
  • getCachedBudgetExists() dans BudgetApi — Dériver l'existence du budget depuis le cache de la liste (['budget', 'list']) est élégant : une source de vérité unique, pas de cache séparé à synchroniser.
  • Tests comportementaux — Les nouveaux tests (has-budget.guard.spec.ts, template-details-store.spec.ts) testent des comportements observables. La distinction "fresh vs stale" dans les tests de garde couvre des scénarios réels et est bien pensée.
  • Encapsulation de lines — Le passage de lines public à #lines + lines = this.#lines.asReadonly() dans template-line-store.ts est une amélioration d'encapsulation correcte.

Verdict : CHANGES REQUESTED (1 important à corriger avant merge)

…save

Merge #convertToExistingLine and #syncWithServerData into single #toCleanLine.
Replace mutable index counter with queue, use Map for O(1) update lookups.
Fix early return to include updatedLines/deletedIds/propagation.
// Slow path: cache miss - fetch from API
// Router automatically shows loading indicator during async operation
try {
const hasBudget = await firstValueFrom(budgetApi.checkBudgetExists$());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] Résultat du fallback API non mis en cache (confiance: 85%)

Quand getCachedBudgetExists() retourne null (cache vide) et que checkBudgetExists$() est appelé, le résultat n'est pas écrit dans cache. Si le guard est activé plusieurs fois avant que le PreloadService ait rempli ['budget', 'list'], chaque activation déclenchera un appel HTTP.

En pratique, le PreloadService remplit le cache au login, donc ce cas est rare. Si l'on veut l'éliminer complètement :

Suggested change
const hasBudget = await firstValueFrom(budgetApi.checkBudgetExists$());
try {
const hasBudget = await firstValueFrom(budgetApi.checkBudgetExists$());
return hasBudget ? true : redirectToCompleteProfile();

await this.#api.deleteAccount();
}

reset(): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] reset() vide le cache de UserSettingsApi mais pas l'état local du resource (confiance: 82%)

reset() appelle this.#api.cache.clear(), ce qui vide le DataCache de UserSettingsApi. Cependant, #settingsResource (un cachedResource) peut conserver son value() en mémoire jusqu'au prochain rechargement. Sur un logout, l'utilisateur précédent pourrait voir brièvement les données du précédent session si le composant est réutilisé.

Vérifier si cachedResource expose un reload() ou un set(null) pour vider l'état du resource en même temps que le cache.


store = TestBed.inject(UserSettingsStore);
store.reload();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Important] Tests utilisant setTimeout(resolve, 100) au lieu de vi.waitFor ou TestBed.flushEffects() (confiance: 88%)

Les tests de UserSettingsStore utilisent await new Promise((resolve) => setTimeout(resolve, 100)) pour attendre le chargement asynchrone. Cela rend les tests fragiles (dépendent du timing réel) et lents. Le pattern établi dans le projet est d'utiliser await vi.waitFor(() => expect(...)) ou TestBed.flushEffects() suivi de vi.waitFor.

Ce pattern setTimeout(100) est utilisé aux lignes 74, 86, 97, 103 de ce fichier et dans template-details-store.spec.ts.

@claude
Copy link
Contributor

claude bot commented Mar 18, 2026

Review PR #373

Résumé : 0 bloquant — 1 important — 2 suggestions

Bloquants

Aucun

Importants

  • user-settings-store.spec.ts:L74 — Tests utilisant setTimeout(resolve, 100) au lieu de vi.waitFor ou TestBed.flushEffects(). Ce pattern est fragile (flaky sur CI lent) et contraire au pattern établi dans le projet. Même problème dans template-details-store.spec.ts.

Suggestions

  • has-budget.guard.ts:L24 — Le fallback checkBudgetExists$() ne met pas son résultat en cache. Impact mineur car le PreloadService remplit le cache au login, mais à surveiller si le timing change.

  • user-settings-store.ts:L58reset() vide le DataCache mais n'invalide pas l'état interne du cachedResource. Vérifier si ngx-ziflux expose un mécanisme pour réinitialiser le resource afin d'éviter qu'une donnée de session précédente ne reste visible après logout.

Points positifs

  • Migration ziflux complète et cohérente : suppression de 1900+ lignes de code custom (DataCache, BudgetInvalidationService, HasBudgetCache) sans régression fonctionnelle visible.
  • getCachedBudgetExists() : dériver l'existence du budget depuis le cache de liste est élégant et élimine la désynchronisation entre deux sources.
  • Logique SWR du guard bien pensée : fresh=true (confiance totale) / stale+data=true (optimisme justifié) / stale+data=false (revalidation). Cas testés.
  • profile-setup.service.ts : la population optimiste invalidate + set après création garantit le fast-path du guard sans attendre le PreloadService.
  • Migration vers cachedMutation + invalidateKeys dans template-line-store.ts propre et conforme au pattern ziflux documenté.
  • Tests bien alignés : suppression des assertions sur les détails d'implémentation internes au profit des comportements observables.

Verdict : APPROVE — la PR est mergeable. Le point important sur les setTimeout dans les tests mérite un suivi mais ne bloque pas le merge.

- Replace manual #isLoading signal with #bulkSaveMutation.isPending (TemplateLineStore)
- Use .asReadonly() instead of computed(() => signal()) wrappers
- Derive netBalance from totals instead of recomputing (TemplateDetailsStore)
- Remove unused transactions alias, update consumers
- Wrap deleteTemplate mutation in method, expose deleteTemplateError signal (BudgetTemplatesStore)
- Fix selectedTemplate to derive from resource instead of stale copy
- Simplify toggleCheck guard in BudgetDetailsStore
- Make isSettingsLoading private (DashboardStore)
- Standardize error sentinel from '' to null (CompleteProfileStore)
- Remove dead loadSingleTemplateTotals method (TemplateStore)
- Update test mocks to match new APIs

All 1590 tests pass. Adversarial review: 0 findings.
expect(mockBudgetInvalidation.invalidate).toHaveBeenCalledOnce();
expect(mockUserSettingsApi.reload).toHaveBeenCalledOnce();
expect(mockBudgetApi.cache.invalidate).toHaveBeenCalledOnce();
expect(mockUserSettingsStore.reload).toHaveBeenCalledOnce();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[IMPORTANT] Assertion manquante : budgetTemplatesApi.cache.invalidate non testée (confiance: 97%)

Le service appelle bien this.#budgetTemplatesApi.cache.invalidate(['templates']) en production (page-lifecycle-recovery.service.ts:193), mais aucun test n'assert cet appel. Si cette ligne est accidentellement supprimée, les templates restent stale indéfiniment après un retour sur l'onglet — sans que les tests échouent.

Suggested change
expect(mockUserSettingsStore.reload).toHaveBeenCalledOnce();
expect(mockBudgetApi.cache.invalidate).toHaveBeenCalledOnce();
expect(mockBudgetTemplatesApi.cache.invalidate).toHaveBeenCalledOnce();
expect(mockUserSettingsStore.reload).toHaveBeenCalledOnce();

Comment on lines +79 to +80
this.#budgetApi.cache.invalidate(['budget']);
this.#budgetApi.cache.set(['budget', 'list'], [budget]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION] Manipulation directe du cache — incohérence avec le pattern cachedMutation (confiance: 85%)

Le reste de la codebase utilise cachedMutation avec invalidateKeys pour orchestrer invalidation + mise à jour du cache. Ici, on manipule cache directement depuis un service de core.

L'ordre actuel est correct (invalidate → set : les entrées stale sont immédiatement écrasées par la donnée fraîche), mais le pattern diffère du reste. Si la logique évolue (ex: race condition entre l'appel set et un cachedResource déjà en refetch), le comportement devient imprévisible.

Alternative : si ProfileSetupService avait accès à un store (ou si BudgetListStore exposait une méthode addBudget), on pourrait déléguer la mise à jour du cache optimiste à cachedMutation. À défaut, un commentaire expliquant pourquoi ce pattern direct est utilisé ici serait utile.

Comment on lines +571 to +572
"totalIncome": "Revenus total :",
"totalExpenses": "Dépenses total :",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION] Accord grammatical incorrect en français (confiance: 95%)

"Revenus total :""total" ne s'accorde pas avec "revenus" (masculin pluriel). "Dépenses total :" → idem, "total" ne s'accorde pas avec "dépenses" (féminin pluriel).

Suggested change
"totalIncome": "Revenus total :",
"totalExpenses": "Dépenses total :",
"totalIncome": "Total des revenus :",
"totalExpenses": "Total des dépenses :",

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant