Quand le catalogue seede ne couvre pas un ingredient, l'utilisateur pouvait etre bloque (creation manuelle) ou perdre silencieusement la ligne (import). Une ligne de recette accepte desormais `placeholderName` (texte libre) au lieu de `ingredientId` : l'API cree une ligne `Ingredient` `isPlaceholder` (cle `placeholder:<uuid>`, `displayName`, `createdById`) dans la transaction de la recette, et emet `ingredient.placeholder_created`. Ces lignes sont exclues de `GET /reference/ingredients` et de `ingredient-matcher`. Front : bouton "Ajouter << ... >>" dans l'etat vide de `IngredientPicker` (formulaire + import), badge "a completer" sur la ligne, helper `ingredientLabel` applique partout ou un libelle d'ingredient est rendu. Admin : `/admin/catalog/*` (+ page `apps/admin-web`) liste les placeholders regroupes par nom normalise, "marquer traite" (`reviewedAt`) et purge des orphelins. La promotion en vraie entree catalogue reste manuelle. Migration `ingredient_placeholder` ecrite a la main (Postgres indisponible). Suites Mocha DB-backed ecrites, non executees en session ; test pur `normalizePlaceholderName` + Cypress admin-web/web verts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
27 lines
1,018 B
TypeScript
27 lines
1,018 B
TypeScript
import { prisma } from "../db/prisma.js";
|
|
import { pruneOrphanPlaceholders } from "../modules/admin/admin-catalog.service.js";
|
|
|
|
/**
|
|
* Deletes every placeholder `Ingredient` row (`Ingredient.isPlaceholder`)
|
|
* that no recipe references any more — the debris left behind when a recipe
|
|
* edit drops a placeholder line (the `RecipeIngredient` row goes, the
|
|
* `Ingredient` row stays). The admin catalog view has a button for this
|
|
* too; this script is the same operation for a cron / one-off cleanup:
|
|
*
|
|
* pnpm --filter api exec tsx src/scripts/prune-orphan-placeholders.ts
|
|
*
|
|
* Delegates to `admin-catalog.service.ts` so the "what counts as an orphan"
|
|
* rule lives in exactly one place.
|
|
*/
|
|
async function main(): Promise<void> {
|
|
const { deleted } = await pruneOrphanPlaceholders();
|
|
console.info(`Pruned ${deleted} orphan placeholder ingredient(s).`);
|
|
}
|
|
|
|
main()
|
|
.then(() => prisma.$disconnect())
|
|
.catch(async (err) => {
|
|
console.error(err);
|
|
await prisma.$disconnect();
|
|
process.exit(1);
|
|
});
|