diff --git a/src/lib/components/BudgetConnexionModal.svelte b/src/lib/components/BudgetConnexionModal.svelte index 9db434374cad01d28d98be2e6963c372dd87a967..24c1995a7e8fccf3497c35b2d79eb797d1dcff1b 100644 --- a/src/lib/components/BudgetConnexionModal.svelte +++ b/src/lib/components/BudgetConnexionModal.svelte @@ -6,23 +6,14 @@ Transition, TransitionChild, } from "@rgossiaux/svelte-headlessui" - import { getContext } from "svelte" - import type { Writable } from "svelte/types/runtime/store" import { browser } from "$app/environment" import { page } from "$app/stores" - import CopyClipboard from "$lib/components/CopyClipboard.svelte" - import type { ParametricReform } from "$lib/reforms" + import type { CachedSimulation } from "$lib/simulations" export let isOpen = false - let cachedSimulations = [] - let clipboardElement: HTMLElement - let isSimulationShared = false - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - let url = "" + let cachedSimulations: CachedSimulation[] = [] $: if (browser) { fetchCachedSimulations() @@ -40,44 +31,6 @@ cachedSimulations = index } - function copyLink() { - const copyClipboard = new CopyClipboard({ - target: clipboardElement, - props: { value: url }, - }) - copyClipboard.$destroy() - } - - async function onChange({ target }: Event) { - if ((target as HTMLInputElement).checked) { - const urlString = "/simulations_budget" - const res = await fetch(urlString, { - body: JSON.stringify({ - parametricReform: $parametricReform, - }), - headers: { - Accept: "application/json", - "Content-Type": "application/json; charset=utf-8", - }, - method: "POST", - }) - if (!res.ok) { - console.error( - `Error ${ - res.status - } while creating a share link at ${urlString}\n\n${await res.text()}`, - ) - return - } - const { token } = await res.json() - url = new URL( - `/simulations_budget/${token}`, - $page.data.baseUrl, - ).toString() - console.log(url) - } - } - function onClose() { isOpen = false } diff --git a/src/lib/components/BudgetSimulationSharingModal.svelte b/src/lib/components/BudgetSimulationSharingModal.svelte index aeb58977e66515a2356ea4186f76a46ee02edeae..c247fa95c16c012da27ea97f0d3b506d9f948d8c 100644 --- a/src/lib/components/BudgetSimulationSharingModal.svelte +++ b/src/lib/components/BudgetSimulationSharingModal.svelte @@ -14,6 +14,7 @@ import type { DisplayMode } from "$lib/displays" import CopyClipboard from "$lib/components/CopyClipboard.svelte" import type { ParametricReform } from "$lib/reforms" + import { budgetEditableParametersName } from "$lib/variables" export let displayMode: DisplayMode export let isOpen = false @@ -22,18 +23,42 @@ BudgetSimulation | undefined > let clipboardElement: HTMLElement + let hasClickedCopy = false let isSimulationShared = false const parametricReform = getContext( "parametricReform", ) as Writable<ParametricReform> + let shareButtons = [ + { + icon: "ri-twitter-fill", + title: "Partager sur Twitter", + link: "https://twitter.com/intent/tweet?text=", + }, + { + icon: "ri-linkedin-fill", + title: "Partager sur LinkedIn", + link: "https://www.linkedin.com/sharing/share-offsite/?url=", + }, + { + icon: "ri-mail-fill", + title: "Partager par e-mail", + link: "mailto:?body=", + }, + ] let url = "" function copyLink() { + if (hasClickedCopy) { + return + } + const copyClipboard = new CopyClipboard({ target: clipboardElement, props: { value: url }, }) copyClipboard.$destroy() + hasClickedCopy = true + setTimeout(() => (hasClickedCopy = false), 2500) } async function onChange({ target }: Event) { @@ -43,7 +68,15 @@ body: JSON.stringify({ budgetSimulation: $budgetSimulation?.result, displayMode, - parametricReform: $parametricReform, + parametricReform: Object.entries($parametricReform).reduce( + (filtered, [parameterName, value]) => { + if (budgetEditableParametersName.has(parameterName)) { + filtered[parameterName] = value + } + return filtered + }, + {}, + ), }), headers: { Accept: "application/json", @@ -69,6 +102,7 @@ function onClose() { isOpen = false + isSimulationShared = false } </script> @@ -163,16 +197,45 @@ /> La simulation est publiƩe : </span> {/if} - <div class="flex justify-center"> + <div class="flex justify-center gap-4"> <div bind:this={clipboardElement} /> <button - class="flex items-center gap-2 py-2 px-7 shadow-lg bg-le-bleu disabled:bg-gray-200 disabled:text-gray-400 disabled:shadow-none hover:opacity-90 active:opacity-80 rounded text-white text-sm font-bold tracking-[0.085em] uppercase" + class="relative shadow-lg bg-le-bleu disabled:bg-gray-200 disabled:text-gray-400 disabled:shadow-none hover:opacity-90 active:opacity-80 rounded text-white text-sm font-bold tracking-[0.085em] uppercase" disabled={!isSimulationShared} on:click={copyLink} + title="Copier le lien de la simulation" > - Copier le lien - <iconify-icon class="text-lg rotate-45" icon="ri-link" /> + <span + class="flex justify-center items-center gap-2 py-2 px-7" + class:invisible={hasClickedCopy} + > + Copier le lien + <iconify-icon class="text-lg rotate-45" icon="ri-link" /> + </span> + <span + class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full h-full flex justify-center items-center gap-2" + class:invisible={!hasClickedCopy} + > + Lien copiƩ + <iconify-icon class="text-lg" icon="ri-check-line" /> + </span> </button> + + {#each shareButtons as shareButton} + <a + class="w-9 h-9 flex justify-center items-center shadow-lg bg-le-bleu hover:opacity-90 active:opacity-80 rounded text-white text-sm font-bold tracking-[0.085em] uppercase" + class:!bg-gray-200={!isSimulationShared} + class:!text-gray-400={!isSimulationShared} + class:!shadow-none={!isSimulationShared} + href={isSimulationShared + ? `${shareButton.link}${url}` + : undefined} + target="_blank" + title={shareButton.title} + > + <iconify-icon class="text-lg" icon={shareButton.icon} /> + </a> + {/each} </div> </div> </DialogDescription> diff --git a/src/lib/components/IntroWarningBillModale.svelte b/src/lib/components/IntroWarningBillModale.svelte index 6fe4eea5dd7692930fc18e65e557c211ce026962..13b1183542731d9ffa70397bbe9aa758e928c48f 100644 --- a/src/lib/components/IntroWarningBillModale.svelte +++ b/src/lib/components/IntroWarningBillModale.svelte @@ -6,61 +6,11 @@ Transition, TransitionChild, } from "@rgossiaux/svelte-headlessui" - import { getContext } from "svelte" - import type { Writable } from "svelte/types/runtime/store" - import { page } from "$app/stores" - import CopyClipboard from "$lib/components/CopyClipboard.svelte" - import type { ParametricReform } from "$lib/reforms" import { browser } from "$app/environment" export let isOpen = false - let clipboardElement: HTMLElement - - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - let url = "" - - function copyLink() { - const copyClipboard = new CopyClipboard({ - target: clipboardElement, - props: { value: url }, - }) - copyClipboard.$destroy() - } - - async function onChange({ target }: Event) { - if ((target as HTMLInputElement).checked) { - const urlString = "/simulations_budget" - const res = await fetch(urlString, { - body: JSON.stringify({ - parametricReform: $parametricReform, - }), - headers: { - Accept: "application/json", - "Content-Type": "application/json; charset=utf-8", - }, - method: "POST", - }) - if (!res.ok) { - console.error( - `Error ${ - res.status - } while creating a share link at ${urlString}\n\n${await res.text()}`, - ) - return - } - const { token } = await res.json() - url = new URL( - `/simulations_budget/${token}`, - $page.data.baseUrl, - ).toString() - console.log(url) - } - } - function onClose() { isOpen = false } diff --git a/src/lib/components/ReformsChanges.svelte b/src/lib/components/ReformsChanges.svelte index 81ea83b969065f6ead6aadf518bb88c63ca9d4aa..5188ce25bb4288d8f2de41250b32b7f5e5fedf04 100644 --- a/src/lib/components/ReformsChanges.svelte +++ b/src/lib/components/ReformsChanges.svelte @@ -69,7 +69,8 @@ <ul class="bg-gray-200 text-xs leading-relaxed text-gray-600"> <li class="my-1"> <Tooltip - arrowClass="bg-le-rouge-bill border-transparent" + arrowClass="bg-white border-le-rouge-bill" + arrowBorderWidth="2px" widthClass="w-60" initialPlacement="right" > @@ -95,7 +96,8 @@ </li> <li class="my-1"> <Tooltip - arrowClass="bg-le-rouge-bill border-transparent" + arrowClass="bg-white border-le-rouge-bill" + arrowBorderWidth="2px" widthClass="w-60" initialPlacement="right" > @@ -121,7 +123,8 @@ </li> <li class="my-1"> <Tooltip - arrowClass="bg-le-rouge-bill border-transparent" + arrowClass="bg-white border-le-rouge-bill" + arrowBorderWidth="2px" widthClass="w-60" initialPlacement="right" > @@ -147,7 +150,8 @@ </li> <li class="my-1"> <Tooltip - arrowClass="bg-le-rouge-bill border-transparent" + arrowClass="bg-white border-le-rouge-bill" + arrowBorderWidth="2px" widthClass="w-60" initialPlacement="right" > @@ -231,6 +235,7 @@ > <Tooltip arrowClass="bg-white border-le-jaune-light" + arrowBorderWidth="2px" widthClass="w-60" initialPlacement="right" > @@ -283,6 +288,7 @@ <li class="my-1 py-2 text-xs leading-relaxed text-gray-600"> <Tooltip arrowClass="bg-white border-le-jaune-light" + arrowBorderWidth="2px" widthClass="w-60" initialPlacement="right" > diff --git a/src/lib/components/Tooltip.svelte b/src/lib/components/Tooltip.svelte index 1469d19251071ae12f9cd67de9d52912f7f5f0ea..7408d167a882ba30302453a8cd9030b3064c7f41 100644 --- a/src/lib/components/Tooltip.svelte +++ b/src/lib/components/Tooltip.svelte @@ -9,6 +9,7 @@ export let allowFlip = true export let arrowClass = "" + export let arrowBorderWidth = "1px" export let initialPlacement: Placement = "bottom" export let role = "presentation" export let tag = "div" @@ -121,9 +122,29 @@ : arrowCoords?.x ? `${arrowCoords?.x}px` : undefined} + style="--arrow-border-width: {arrowBorderWidth};" class:after:border-t={placement === "bottom" || placement === "left"} + class:border-t-width={placement === "bottom" || placement === "left"} class:after:border-r={placement === "top" || placement === "left"} + class:border-r-width={placement === "top" || placement === "left"} class:after:border-b={placement === "top" || placement === "right"} + class:border-b-width={placement === "top" || placement === "right"} class:after:border-l={placement === "bottom" || placement === "right"} + class:border-l-width={placement === "bottom" || placement === "right"} ></div> </div> + +<style> + .border-t-width::after { + border-top-width: var(--arrow-border-width); + } + .border-r-width::after { + border-right-width: var(--arrow-border-width); + } + .border-b-width::after { + border-bottom-width: var(--arrow-border-width); + } + .border-l-width::after { + border-left-width: var(--arrow-border-width); + } +</style> diff --git a/src/lib/components/variables/InflationLawButton.svelte b/src/lib/components/variables/InflationLawButton.svelte index df0f4aff2709ed2bd7c4902532715cd46d72e13d..12ede2662d5bb472254b289c0cbbbb6afdff19f1 100644 --- a/src/lib/components/variables/InflationLawButton.svelte +++ b/src/lib/components/variables/InflationLawButton.svelte @@ -12,43 +12,6 @@ : Object.entries(revaluationDetails.values).sort( ([instant1], [instant2]) => instant2.localeCompare(instant1), )[0][1] - - const inflatedParametersDelete = [ - "impot_revenu.bareme_ir_depuis_1945.bareme", - "impot_revenu.calcul_impot_revenu.plaf_qf.decote.seuil_couple", - "impot_revenu.calcul_impot_revenu.plaf_qf.decote.seuil_celib", - "impot_revenu.calcul_impot_revenu.plaf_qf.plafond_avantages_procures_par_demi_part", - "impot_revenu.calcul_revenus_imposables.abat_rni.enfant_marie.montant", - "impot_revenu.calcul_reductions_impots.dons.dons_coluche.plafond", - "impot_revenu.calcul_revenus_imposables.abat_rni.personne_agee_ou_invalide", - "impot_revenu.calcul_revenus_imposables.rpns.micro.microentreprise.regime_micro_bnc.marchandises.plafond", - "impot_revenu.calcul_revenus_imposables.deductions.abatpen", - "impot_revenu.calcul_revenus_imposables.deductions.abatpro", - "impot_revenu.calcul_revenus_imposables.rpns.micro.microentreprise.regime_micro_ba.plafond_recettes", - "impot_revenu.calcul_revenus_imposables.rpns.micro.microentreprise.regime_micro_bnc.services.plafond", - "impot_revenu.calcul_revenus_imposables.charges_deductibles.pensions_alimentaires.plafond", - "prelevements_sociaux.contributions_sociales.csg.remplacement.seuils", - "prestations_sociales.aides_logement.allocations_logement.al_loc2.par_zone", - "prestations_sociales.aides_logement.allocations_logement.al_charge", - "prestations_sociales.aides_logement.allocations_logement.al_etudiant", - "prestations_sociales.aides_logement.allocations_logement.al_plaf_logement_foyer", - "prestations_sociales.aides_logement.allocations_logement.al_loc2.montant_forfaitaire_participation_minimale_po", - "prestations_sociales.aides_logement.allocations_logement.al_param_r0.r0", - "prestations_sociales.aides_logement.ressources.dar_4", - "prestations_sociales.aides_logement.ressources.dar_5", - "prestations_sociales.aides_logement.ressources.dar_11", - "prestations_sociales.aides_logement.ressources.dar_12", - "prestations_sociales.solidarite_insertion.minima_sociaux.ppa.pa_m.montant_de_base", - "prestations_sociales.solidarite_insertion.minima_sociaux.rsa.rsa_m.montant_de_base_du_rsa", - "prestations_sociales.solidarite_insertion.minimum_vieillesse.aspa.plafond_ressources", - "prestations_sociales.solidarite_insertion.minimum_vieillesse.aspa.montant_maximum_annuel", - "prestations_sociales.prestations_etat_de_sante.invalidite.asi.plafond_ressource_couple", - "prestations_sociales.prestations_etat_de_sante.invalidite.asi.plafond_ressource_seul", - "prestations_sociales.aides_jeunes.contrat_engagement_jeune.montants", - "prestations_sociales.prestations_etat_de_sante.invalidite.aah.montant", - "prestations_sociales.prestations_familiales.bmaf.bmaf", - "prelevements_sociaux.pss", - ] </script> <button diff --git a/src/lib/components/variables/InflationLawDetails.svelte b/src/lib/components/variables/InflationLawDetails.svelte index 99c5e8e03ea2a2e84abe1c9400bf4806d2125471..e30e6e334cccf91a8cb757fd1ec40c3f64f0d6c7 100644 --- a/src/lib/components/variables/InflationLawDetails.svelte +++ b/src/lib/components/variables/InflationLawDetails.svelte @@ -13,43 +13,6 @@ export let revaluationDetails: ValueParameter export let revaluationParameter: ScaleParameter | ValueParameter - const inflatedParametersDelete = [ - "impot_revenu.bareme_ir_depuis_1945.bareme", - "impot_revenu.calcul_impot_revenu.plaf_qf.decote.seuil_couple", - "impot_revenu.calcul_impot_revenu.plaf_qf.decote.seuil_celib", - "impot_revenu.calcul_impot_revenu.plaf_qf.plafond_avantages_procures_par_demi_part", - "impot_revenu.calcul_revenus_imposables.abat_rni.enfant_marie.montant", - "impot_revenu.calcul_reductions_impots.dons.dons_coluche.plafond", - "impot_revenu.calcul_revenus_imposables.abat_rni.personne_agee_ou_invalide", - "impot_revenu.calcul_revenus_imposables.rpns.micro.microentreprise.regime_micro_bnc.marchandises.plafond", - "impot_revenu.calcul_revenus_imposables.deductions.abatpen", - "impot_revenu.calcul_revenus_imposables.deductions.abatpro", - "impot_revenu.calcul_revenus_imposables.rpns.micro.microentreprise.regime_micro_ba.plafond_recettes", - "impot_revenu.calcul_revenus_imposables.rpns.micro.microentreprise.regime_micro_bnc.services.plafond", - "impot_revenu.calcul_revenus_imposables.charges_deductibles.pensions_alimentaires.plafond", - "prelevements_sociaux.contributions_sociales.csg.remplacement.seuils", - "prestations_sociales.aides_logement.allocations_logement.al_loc2.par_zone", - "prestations_sociales.aides_logement.allocations_logement.al_charge", - "prestations_sociales.aides_logement.allocations_logement.al_etudiant", - "prestations_sociales.aides_logement.allocations_logement.al_plaf_logement_foyer", - "prestations_sociales.aides_logement.allocations_logement.al_loc2.montant_forfaitaire_participation_minimale_po", - "prestations_sociales.aides_logement.allocations_logement.al_param_r0.r0", - "prestations_sociales.aides_logement.ressources.dar_4", - "prestations_sociales.aides_logement.ressources.dar_5", - "prestations_sociales.aides_logement.ressources.dar_11", - "prestations_sociales.aides_logement.ressources.dar_12", - "prestations_sociales.solidarite_insertion.minima_sociaux.ppa.pa_m.montant_de_base", - "prestations_sociales.solidarite_insertion.minima_sociaux.rsa.rsa_m.montant_de_base_du_rsa", - "prestations_sociales.solidarite_insertion.minimum_vieillesse.aspa.plafond_ressources", - "prestations_sociales.solidarite_insertion.minimum_vieillesse.aspa.montant_maximum_annuel", - "prestations_sociales.prestations_etat_de_sante.invalidite.asi.plafond_ressource_couple", - "prestations_sociales.prestations_etat_de_sante.invalidite.asi.plafond_ressource_seul", - "prestations_sociales.aides_jeunes.contrat_engagement_jeune.montants", - "prestations_sociales.prestations_etat_de_sante.invalidite.aah.montant", - "prestations_sociales.prestations_familiales.bmaf.bmaf", - "prelevements_sociaux.pss", - ] - const numberFormatter = (value: number): string => new Intl.NumberFormat("fr-FR", { style: "decimal", diff --git a/src/lib/components/variables/VariableReferredParameterHeader.svelte b/src/lib/components/variables/VariableReferredParameterHeader.svelte index 43fddf5e885057f39677f53e8a8938eb57ca4ce7..792ee5c9d8da934307470d3ef41d5e5c693f5b64 100644 --- a/src/lib/components/variables/VariableReferredParameterHeader.svelte +++ b/src/lib/components/variables/VariableReferredParameterHeader.svelte @@ -1,26 +1,33 @@ <script lang="ts"> import type { Parameter } from "@openfisca/json-model" import { iterParameterAncestors, ParameterClass } from "@openfisca/json-model" + import smoothScrollIntoView from "scroll-into-view-if-needed" import { afterUpdate, getContext } from "svelte" - import type { SelfTargetAProps } from "$lib/urls" import type { DisplayMode } from "$lib/displays" + import type { SelfTargetAProps } from "$lib/urls" import viewport from "$lib/viewport" - import smoothScrollIntoView from "scroll-into-view-if-needed" export let depth: number export let displayMode: DisplayMode export let parameter: Parameter + let htmlElement: HTMLElement | undefined + let isCopiedSuccessfully = false + let isElementInViewport = false + const onCopyParameterLink = getContext("onCopyParameterLink") as ( + parameterName: string, + ) => void const newSelfTargetAProps = getContext("newSelfTargetAProps") as ( url: string, ) => SelfTargetAProps - const onCopyParameterLink = getContext("onCopyParameterLink") as ( - parameterName: string, - ) => void + $: isParameterSelected = urlHash === parameter.name + + $: urlHash = displayMode.parameterHash?.includes("-") + ? displayMode.parameterHash?.split("-")[0] + : displayMode.parameterHash - let isCopiedSuccessfully = false function copyLink() { if (parameter.name !== undefined) { onCopyParameterLink(parameter.name) @@ -29,13 +36,6 @@ } } - $: urlHash = displayMode.parameterHash?.includes("-") - ? displayMode.parameterHash?.split("-")[0] - : displayMode.parameterHash - $: isParameterSelected = urlHash === parameter.name - let htmlElement - let isElementInViewport = false - afterUpdate(() => { if (htmlElement !== undefined && isParameterSelected) { smoothScrollIntoView(htmlElement, { behavior: "smooth", block: "center" }) diff --git a/src/lib/simulations.ts b/src/lib/simulations.ts index 6eba71d7644295578b7bcadc84610f0b130c9c77..1722aca0b02f1d182c8895eb6db74b21a37d3f71 100644 --- a/src/lib/simulations.ts +++ b/src/lib/simulations.ts @@ -1,3 +1,10 @@ +export interface CachedSimulation { + date: string + hash: string + parameters: string[] + title: string +} + export interface EntitySimulation { [key: string]: { [date: string]: number } | string[] } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 96d0b04a5f20f13d4f8538dabc365155b825614f..47911a032d48a9a8b78b708d8b00bc339474f08b 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1416,8 +1416,9 @@ <button class="z-30 absolute -bottom-4 right-5 flex items-center gap-2 py-2 px-5 shadow-lg bg-white hover:bg-gray-100 active:bg-gray-200 rounded border border-le-bleu text-le-bleu text-sm font-bold tracking-[0.085em] uppercase transition-all duration-200 ease-out-back disabled:opacity-0 disabled:scale-90" on:click={() => (isBudgetSimulationModalOpen = true)} - disabled={Object.keys($parametricReform).length === 0 || - $budgetSimulation === undefined} + disabled={Object.keys($parametricReform).filter((parameterName) => + budgetEditableParametersName.has(parameterName), + ).length === 0 || $budgetSimulation === undefined} > Partager cette simulation <iconify-icon class="text-lg" icon="ri-send-plane-2-line" /> @@ -1700,7 +1701,9 @@ displayMode.budget && !displayMode.mobileLaw && displayMode.parametersVariableName !== undefined && - Object.keys($parametricReform).length > 0 && + Object.keys($parametricReform).filter((parameterName) => + budgetEditableParametersName.has(parameterName), + ).length > 0 && !deepEqual($budgetSimulationCache?.parametricReform, $parametricReform)} <div class="flex flex-col items-center md:block fixed bottom-0 md:bottom-auto md:top-12 2xl:top-14 md:right-3 bg-le-jaune-light z-40 rounded-t-lg md:rounded-t-none md:rounded-b-lg shadow-md mx-5 md:mx-0 inset-x-0 md:inset-x-auto px-3 lg:px-5 pt-3 pb-6 md:pt-6 md:pb-3 lg:pt-8 transition-transform duration-[350ms] ease-out-back md:delay-0" diff --git a/src/routes/simulations_budget/+server.ts b/src/routes/simulations_budget/+server.ts index da919b8bd50ebc9c73ae90a723025d4f5508225d..78e675b01993ed767988d1b65f07ba28ea60b452 100644 --- a/src/routes/simulations_budget/+server.ts +++ b/src/routes/simulations_budget/+server.ts @@ -2,11 +2,12 @@ import { auditRequire, cleanAudit, type Audit } from "@auditors/core" import { error, json } from "@sveltejs/kit" import fs from "fs-extra" import path from "path" - import { XXH64 } from "xxh3-ts" import { getParameter, rootParameter } from "$lib/parameters" +import type { ParametricReform } from "$lib/reforms" import config from "$lib/server/config" +import type { CachedSimulation } from "$lib/simulations" import type { RequestHandler } from "./$types" @@ -70,9 +71,13 @@ export const POST: RequestHandler = async ({ request, url }) => { throw error(400, `Invalid body: ${JSON.stringify(bodyError, null, 2)}`) } - const modifiedParameters = Object.keys(body.parametricReform).map( - (parameterName) => getParameter(rootParameter, parameterName)?.title, - ) + const modifiedParametersTitles = Object.keys( + (body as { parametricReform: ParametricReform }).parametricReform, + ).map((parameterName) => getParameter(rootParameter, parameterName)?.title) + + const modifiedParametersNames = Object.keys( + (body as { parametricReform: ParametricReform }).parametricReform, + ).map((parameterName) => getParameter(rootParameter, parameterName)?.name) const bodyJson = JSON.stringify(body, null, 2) @@ -91,20 +96,22 @@ export const POST: RequestHandler = async ({ request, url }) => { } const indexDir = path.join(simulationsBudgetDir, "index.json") - const indexContents = (await fs.pathExists(indexDir)) + const indexContents: CachedSimulation[] = (await fs.pathExists(indexDir)) ? await fs.readJson(indexDir) : [] - await fs.writeFile( - indexDir, - JSON.stringify([ - ...indexContents, - { - date: new Intl.DateTimeFormat("fr-FR").format(new Date()), - hash: digest, - title: modifiedParameters.join(" | "), - }, - ]), + const contents: CachedSimulation[] = [ + ...indexContents, + { + date: new Intl.DateTimeFormat("fr-FR").format(new Date()), + hash: digest, + parameters: modifiedParametersNames, + title: modifiedParametersTitles.join(" | "), + } as CachedSimulation, + ].filter( + (value, index, self) => + index === self.findIndex((el) => el.hash === value.hash), ) + await fs.writeFile(indexDir, JSON.stringify(contents)) return json({ token: digest }) } diff --git a/src/routes/simulations_budget/index/+server.ts b/src/routes/simulations_budget/index/+server.ts index 0059bb9624541eb679c4a3644adc27623aa2ffe3..b8820b7dfd379daba454d35398da1464ce447369 100644 --- a/src/routes/simulations_budget/index/+server.ts +++ b/src/routes/simulations_budget/index/+server.ts @@ -3,6 +3,7 @@ import fs from "fs-extra" import path from "path" import config from "$lib/server/config" +import type { CachedSimulation } from "$lib/simulations" import type { RequestHandler } from "./$types" @@ -12,6 +13,8 @@ export const POST: RequestHandler = async () => { const indexDir = path.join(simulationsBudgetDir, "index.json") return json({ - index: (await fs.pathExists(indexDir)) ? await fs.readJson(indexDir) : [], + index: (await fs.pathExists(indexDir)) + ? ((await fs.readJson(indexDir)) as CachedSimulation[]) + : [], }) }