mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-04-06 21:41:42 +02:00
Capitalisation complète — app-alexandrie & app-template-resto (23-03-2026)
Intègre ~50 entrées depuis 95_a_capitaliser.md vers les fichiers validés :
- backend risques : +15 (GET sans authz, TOCTOU tenantId, TTL UTC, AdminRoleGuard, P3014...)
- backend patterns : P2002 amendé (create+update) + 10 nouveaux (Decimal, URL safe, EN enforcement...)
- frontend risques : +21 (defaultValue/key, useTransition global, consent state, Tailwind invalide...)
- frontend patterns : +6 (click-to-load, toggle optimiste, Server Action retourne entité...)
- debug/postmortem : export{fn} ne crée pas de binding local
95_a_capitaliser.md remis à l'état initial vide.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ Il sert de **mémoire durable** pour éviter :
|
||||
- de redélibérer éternellement sur des sujets déjà tranchés,
|
||||
- de propager des “bonnes pratiques” théoriques non éprouvées.
|
||||
|
||||
Dernière mise à jour : 20-03-2026
|
||||
Dernière mise à jour : 23-03-2026
|
||||
|
||||
---
|
||||
|
||||
@@ -29,6 +29,11 @@ Dernière mise à jour : 20-03-2026
|
||||
- [Tests de styles React Native sans renderer JSX](#pattern-tests-styles-sans-renderer)
|
||||
- [Export des styles de composant pour réutilisation partielle](#pattern-export-styles-composant)
|
||||
- [Token typography par usage sémantique (React Native)](#pattern-token-typography-semantique)
|
||||
- [Click-to-load strict pour les embeds tiers (iframe/widget)](#pattern-click-to-load-embeds-tiers)
|
||||
- [Toggle optimiste avec rollback (React Server Action)](#pattern-toggle-optimiste-rollback)
|
||||
- [Server Action retournant l'entité — élimination de `router.refresh()` sur create/edit](#pattern-server-action-retourne-entite)
|
||||
- [ESLint flat config avec presets Next.js (`eslint.config.mjs`)](#pattern-eslint-flat-config-nextjs)
|
||||
- [Grilles 2 colonnes FR/EN — mobile-first](#pattern-grilles-2-colonnes-mobile-first)
|
||||
|
||||
---
|
||||
|
||||
@@ -634,3 +639,192 @@ mediumText12: { fontSize: 12, fontWeight: ‘500’ }, // ambigu, réutilisé
|
||||
- on met à jour la date
|
||||
- on précise le nouveau contexte
|
||||
- En cas de doute → le pattern n’entre pas encore ici
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-click-to-load-embeds-tiers"></a>
|
||||
## Pattern : Click-to-load strict pour les embeds tiers (iframe/widget)
|
||||
|
||||
### Synthèse
|
||||
|
||||
- **Objectif** : ne charger aucun service tiers sans action explicite de l’utilisateur (performance + consentement implicite).
|
||||
- **Contexte** : site/webapp avec modules de réservation, map, chat ou tout embed iframe à la demande.
|
||||
- **Quand l’utiliser** : dès qu’un embed tiers est chargé à la demande (pas au premier rendu).
|
||||
- **Quand l’éviter** : si l’embed est central à la page et doit être visible immédiatement.
|
||||
|
||||
### Analyse
|
||||
|
||||
- **Avantages** :
|
||||
- LCP non pollué par des tiers (performance-first)
|
||||
- Aucun tiers ne reçoit de données utilisateur sans action volontaire (consentement implicite)
|
||||
- Fallback toujours disponible en cas d’erreur iframe
|
||||
- **Limites / vigilance** :
|
||||
- Le fallback (lien externe + `tel:`) doit être actionnable même si l’embed échoue
|
||||
|
||||
### Validation
|
||||
|
||||
- Validé le : 21-03-2026
|
||||
- Contexte technique : React / Next.js — app-template-resto
|
||||
|
||||
### Implémentation
|
||||
|
||||
```tsx
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [errored, setErrored] = useState(false);
|
||||
|
||||
if (errored) return <a href={url}>Ouvrir {label}</a>;
|
||||
|
||||
return (
|
||||
<>
|
||||
{!loaded && <button onClick={() => setLoaded(true)}>Charger {label}</button>}
|
||||
{loaded && <iframe src={url} onError={() => setErrored(true)} />}
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-toggle-optimiste-rollback"></a>
|
||||
## Pattern : Toggle optimiste avec rollback (React Server Action)
|
||||
|
||||
### Synthèse
|
||||
|
||||
- **Objectif** : masquer la latence serveur sur un toggle boolean en mettant à jour l’UI immédiatement, avec rollback en cas d’erreur.
|
||||
- **Contexte** : toggles boolean (visibilité, disponibilité, settings) où la latence doit être masquée.
|
||||
- **Quand l’utiliser** : toggles sans besoin de re-fetcher l’entité entière après mutation.
|
||||
- **Quand l’éviter** : mutations qui retournent des données complexes → préférer le pattern "Server Action retournant l’entité".
|
||||
|
||||
### Validation
|
||||
|
||||
- Validé le : 21-03-2026
|
||||
- Contexte technique : React / Next.js App Router — app-template-resto
|
||||
|
||||
### Implémentation
|
||||
|
||||
```tsx
|
||||
const [optimistic, setOptimistic] = useState(initialValue);
|
||||
|
||||
async function handleToggle() {
|
||||
const prev = optimistic;
|
||||
setOptimistic(!prev); // update immédiat
|
||||
try {
|
||||
await toggleAction(!prev);
|
||||
router.refresh(); // synchronise le Server Component parent
|
||||
} catch {
|
||||
setOptimistic(prev); // rollback si erreur
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-server-action-retourne-entite"></a>
|
||||
## Pattern : Server Action retournant l’entité — élimination de `router.refresh()` sur create/edit
|
||||
|
||||
### Synthèse
|
||||
|
||||
- **Objectif** : mettre à jour l’état local directement avec les données réelles retournées par le serveur, sans round-trip SSR supplémentaire.
|
||||
- **Contexte** : liste d’items managée côté client (`useState`) avec création et modification via Server Actions.
|
||||
- **Quand l’utiliser** : create et edit d’entités dans une liste. Plus performant que toggle optimiste + `router.refresh()`.
|
||||
- **Quand l’éviter** : simples toggles boolean → le pattern optimiste avec rollback suffit.
|
||||
|
||||
### Analyse
|
||||
|
||||
- **Avantages vs toggle optimiste + `router.refresh()` :**
|
||||
- Zéro aller-retour SSR supplémentaire (~500ms–2s économisés sur mobile)
|
||||
- État local garanti cohérent avec la DB (données réelles, pas calculées localement)
|
||||
- Pas de flash de rechargement
|
||||
- **Limites / vigilance** :
|
||||
- `revalidatePath` reste nécessaire pour invalider le cache des pages publiques
|
||||
|
||||
### Validation
|
||||
|
||||
- Validé le : 22-03-2026
|
||||
- Contexte technique : React / Next.js App Router — app-template-resto story 3.8
|
||||
|
||||
### Implémentation
|
||||
|
||||
```typescript
|
||||
// Repository — retourne l’entité complète
|
||||
export async function createItem(tenantId: string, data: Input): Promise<ItemRow> {
|
||||
return prisma.item.create({ data: { tenantId, ...data }, select: { ...fullSelect } });
|
||||
}
|
||||
|
||||
// Action — retourne la donnée au client
|
||||
export async function createItemAction(formData: FormData): Promise<ItemRow> {
|
||||
const actor = await requireOwner();
|
||||
const item = await createItem(actor.tenantId, input);
|
||||
revalidatePath("/dashboard/..."); // invalider cache pages publiques
|
||||
return item; // ← clé : retourner l’entité
|
||||
}
|
||||
|
||||
// Client — mise à jour locale sans round-trip SSR
|
||||
const created = await createItemAction(formData);
|
||||
setItems((prev) => [...prev, created]); // pas de router.refresh()
|
||||
```
|
||||
|
||||
**Pour les entités avec relations :** utiliser un helper `findItemById(tenantId, id)` appelé après la mutation pour retourner la forme complète avec les relations résolues.
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-eslint-flat-config-nextjs"></a>
|
||||
## Pattern : ESLint flat config avec presets Next.js (`eslint.config.mjs`)
|
||||
|
||||
### Synthèse
|
||||
|
||||
- **Objectif** : éviter les bugs de compatibilité de l’ancien `.eslintrc` avec Next.js récent.
|
||||
- **Contexte** : projet Next.js récent utilisant déjà le flat config ESLint.
|
||||
- **Quand l’utiliser** : nouveau projet Next.js ou migration ESLint.
|
||||
- **Quand l’éviter** : si le projet doit rester compatible avec des outils legacy ESLint.
|
||||
|
||||
### Validation
|
||||
|
||||
- Validé le : 16-03-2026
|
||||
- Contexte technique : Next.js 16+ / ESLint flat config — app-template-resto
|
||||
|
||||
### Implémentation
|
||||
|
||||
```javascript
|
||||
// eslint.config.mjs
|
||||
import nextPlugin from "@next/eslint-plugin-next";
|
||||
|
||||
export default [
|
||||
...nextPlugin.configs["core-web-vitals"],
|
||||
...nextPlugin.configs["typescript"],
|
||||
{
|
||||
rules: {
|
||||
// overrides ciblés ici
|
||||
},
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-grilles-2-colonnes-mobile-first"></a>
|
||||
## Pattern : Grilles 2 colonnes FR/EN — mobile-first
|
||||
|
||||
### Synthèse
|
||||
|
||||
- **Objectif** : afficher les champs FR + EN côte à côte sur desktop, en colonne unique sur mobile.
|
||||
- **Contexte** : formulaires dashboard avec champs bilingues FR/EN côte à côte.
|
||||
- **Quand l’utiliser** : tout formulaire avec colonnes parallèles sur un projet mobile-first.
|
||||
- **Quand l’éviter** : si les champs sont indépendants et n’ont pas de relation visuelle FR/EN.
|
||||
|
||||
### Validation
|
||||
|
||||
- Validé le : 22-03-2026
|
||||
- Contexte technique : Tailwind CSS / React — app-template-resto
|
||||
|
||||
### Implémentation
|
||||
|
||||
```html
|
||||
<!-- ✅ Mobile-first — colonne unique sur < 640px, 2 colonnes sur ≥ 640px -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<input placeholder="Nom (FR)" />
|
||||
<input placeholder="Name (EN)" />
|
||||
</div>
|
||||
|
||||
<!-- ❌ À éviter — 2 colonnes trop étroites sur mobile -->
|
||||
<div class="grid grid-cols-2 gap-4">...</div>
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user