diff --git a/README.md b/README.md index c73a19c2bc5ca1687fcf1b4545d4b34ec0cbd95a..225ae7d74ddf2b264183e92e15b54286a083afa5 100644 --- a/README.md +++ b/README.md @@ -58,18 +58,18 @@ Ajouter en haut du fichier : Et ensuite utiliser un test : ```js -{#if !$billActive} +{#if !billActive} "COUCOU qui s'affiche si le PLF n'est pas activé" {/if} ``` ```js -{#if $billActive} +{#if billActive} "COUCOU qui s'affiche si le PLF est activé" {/if} ``` -$billActive => plf activé +billActive => plf activé ### Ajouter une variable calculée dans le simulateur diff --git a/package-lock.json b/package-lock.json index 3da99dfb0866bc2da527fa1a13993473abd3d943..03f82505fb5eaa81acf2b6303a75b3f07eac654c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,7 +58,7 @@ "sanitize-filename": "^1.6.3", "scroll-into-view-if-needed": "^3.0.10", "slug": "^10.0.0", - "svelte": "^5.0.0", + "svelte": "^5.1.4", "svelte-check": "^4.0.1", "svelte-dnd-action": "^0.9.8", "svelte-floating-ui": "^1.5.3", @@ -6189,11 +6189,10 @@ } }, "node_modules/svelte": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.1.3.tgz", - "integrity": "sha512-Sl8UFHlBvF54aK8MElFvyvaUfPE2REOz6LnhR2pBClCL11MU4qpn4V+KgAggaXxDyrP2iQixvHbtpHqL/zXlSQ==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.1.4.tgz", + "integrity": "sha512-qgHDV7AyvBZa2pbf+V0tnvWrN1LKD8LdUsBkR/SSYVVN6zXexiXnOy5Pjcjft2y/2NJJVa8ORUHFVn3oiWCLVQ==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", diff --git a/package.json b/package.json index a9eb13e766a8102ec035f0a0980251432c3b83ce..ec93f406646fe16cf0a9ee129bfe7e5e70ab6f4d 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "sanitize-filename": "^1.6.3", "scroll-into-view-if-needed": "^3.0.10", "slug": "^10.0.0", - "svelte": "^5.0.0", + "svelte": "^5.1.4", "svelte-check": "^4.0.1", "svelte-dnd-action": "^0.9.8", "svelte-floating-ui": "^1.5.3", diff --git a/src/lib/calculations.svelte.ts b/src/lib/calculations.svelte.ts index 85ad0fed083d416dfbd319dfb1b98cfcca3cae1c..5ecd8540fd324c515f18f6e7d97981a8b3543486 100644 --- a/src/lib/calculations.svelte.ts +++ b/src/lib/calculations.svelte.ts @@ -1,13 +1,11 @@ import type { Evaluation } from "$lib/decompositions" import type { ParametricReform } from "$lib/reforms" +import publicConfig from "$lib/public_config" +import { calculateTestCases } from "$lib/shared.svelte" import type { VariableValueByCalculationName } from "$lib/variables" -import { entityByKey } from "./entities" -import publicConfig from "./public_config" -import type { SituationWithAxes } from "./situations" export type CalculationName = "amendment" | "bill" | "law" | "revaluation" -const { reformName, revaluationName } = publicConfig export const calculationNames: CalculationName[] = [ "law", "revaluation", @@ -29,276 +27,6 @@ export let requestedCalculations = $state({ situationIndexByCalculationName: {}, }) as RequestedCalculations -function calculateTestCases({}: { - situationIndexByCalculationName: RequestedSituationIndexByCalculationName -}) { - const aggregatedSituation: SituationWithAxes = {} - const axesData: { situationIndex: number; variables: string[] } = - $axes.reduce( - ( - result: { situationIndex?: number; variables: string[] }, - parallelAxes, - ) => { - for (const axis of parallelAxes) { - result.situationIndex = axis.situationIndex - if (result.variables.includes(axis.name)) { - continue - } - result.variables.push(axis.name) - } - return result - }, - { variables: [] }, - ) - const entities = Object.values(entityByKey) - if ( - Object.values(situationIndexByCalculationName).some( - (situationIndex) => situationIndex === null, - ) - ) { - let personIndex = -1 - // Some of the calculations are requested to be done for every situations. - // Aggregate every situations into a single one without calculated variables. - for (const [situationIndex, situation] of $testCases.entries()) { - const inputInstantsByVariableName = - $inputInstantsByVariableNameArray[situationIndex] - const situationPrefix = `Cas type n°${situationIndex + 1} | ` - for (const entity of entities) { - let entitySituation = situation[entity.key_plural as string] - if (entitySituation === undefined) { - continue - } - let aggregatedEntitySituation = - aggregatedSituation[entity.key_plural as string] - if (aggregatedEntitySituation === undefined) { - aggregatedEntitySituation = aggregatedSituation[ - entity.key_plural as string - ] = {} - } - const reservedKeys = getPopulationReservedKeys(entity) - for (const [populationId, population] of Object.entries( - entitySituation, - ).sort(([populationId1], [populationId2]) => - populationId1.localeCompare(populationId2), - )) { - const transformedPopulation: PopulationWithoutId = {} - if (!entity.is_person) { - for (const role of (entity as GroupEntity).roles) { - const personsIdKey = getRolePersonsIdKey(role) - const personsId = population[personsIdKey] as string[] | undefined - if (personsId === undefined) { - continue - } - transformedPopulation[personsIdKey] = personsId.map( - (personId) => situationPrefix + personId, - ) - } - } else { - personIndex++ - } - for (const [variableName, variableValueByInstant] of Object.entries( - population, - )) { - if ( - reservedKeys.has(variableName) || - (entity.is_person && - axesData.situationIndex === personIndex && - axesData.variables.includes(variableName)) - ) { - continue - } - const inputVariableValueByInstant: { - [instant: string]: VariableValue | null - } = {} - const inputInstants = - inputInstantsByVariableName[variableName] ?? new Set<string>() - for (const [instant, variableValue] of Object.entries( - variableValueByInstant as { - [instant: string]: VariableValue | null - }, - )) { - if (!inputInstants.has(instant)) { - // Ignore calculated value. - continue - } - inputVariableValueByInstant[instant] = variableValue - } - if (Object.keys(inputVariableValueByInstant).length > 0) { - transformedPopulation[variableName] = inputVariableValueByInstant - } - } - aggregatedEntitySituation[situationPrefix + populationId] = - transformedPopulation - } - } - } - - if ($axes.length > 0) { - aggregatedSituation.axes = $axes - } - } - - const message = { - period: $year.toString(), - } - const newCalculationByName: CalculationByName = {} - - const lawSituationIndex = situationIndexByCalculationName.law - if (lawSituationIndex !== undefined) { - const calculation: Calculation = (newCalculationByName.law = { - running: true, - }) - let situation: SituationWithAxes - if (lawSituationIndex === null) { - situation = aggregatedSituation - } else { - calculation.situationIndex = lawSituationIndex - situation = cleanSituation($testCases[lawSituationIndex], $axes) - } - calculation.input = { - ...message, - situation, - variables: [ - ...summaryCalculatedVariablesName, - ...otherCalculatedVariablesName, - ...nonVirtualVariablesName, - ], - } - sendTestCasesSimulation("law", calculation.input) // Don't wait for result. - } - - const revaluationSituationIndex = situationIndexByCalculationName.revaluation - if ( - revaluationSituationIndex !== undefined && - revaluationName !== undefined - ) { - const calculation: Calculation = (newCalculationByName.revaluation = { - running: true, - }) - let situation: SituationWithAxes - if (revaluationSituationIndex === null) { - situation = aggregatedSituation - } else { - calculation.situationIndex = revaluationSituationIndex - situation = cleanSituation($testCases[revaluationSituationIndex], $axes) - } - calculation.input = { - ...message, - reform: revaluationName, - situation, - variables: [ - ...summaryCalculatedVariablesName, - ...otherCalculatedVariablesName, - ...(nonVirtualVariablesNameByReformName[revaluationName] ?? - nonVirtualVariablesName), - ], - } - sendTestCasesSimulation("revaluation", calculation.input) // Don't wait for result. - } - - const billSituationIndex = situationIndexByCalculationName.bill - if (billSituationIndex !== undefined && $billActive) { - const calculation: Calculation = (newCalculationByName.bill = { - running: true, - }) - let situation: SituationWithAxes - if (billSituationIndex === null) { - situation = aggregatedSituation - } else { - calculation.situationIndex = billSituationIndex - situation = cleanSituation($testCases[billSituationIndex], $axes) - } - calculation.input = { - ...message, - reform: reformName, - situation, - variables: [ - ...summaryCalculatedVariablesName, - ...otherCalculatedVariablesName, - ...(nonVirtualVariablesNameByReformName[reformName] ?? - nonVirtualVariablesName), - ], - } - sendTestCasesSimulation("bill", calculation.input) // Don't wait for result. - } - - const amendmentSituationIndex = situationIndexByCalculationName.amendment - if (amendmentSituationIndex !== undefined) { - if (Object.keys($parametricReform).length === 0) { - // Remove amendment evaluations from decompositions. - $evaluationByNameArray = $evaluationByNameArray.map( - (evaluationByName, situationIndex): EvaluationByName => { - const updatedEvaluationByName = Object.fromEntries( - Object.entries(evaluationByName).map( - ([variableName, evaluation]) => { - const calculationEvaluationByName = { - ...evaluation.calculationEvaluationByName, - } - delete calculationEvaluationByName["amendment"] - return [ - variableName, - { ...evaluation, calculationEvaluationByName }, - ] - }, - ), - ) - return updateEvaluations( - $decompositionByName, - updatedEvaluationByName, - $testCases[situationIndex].slider?.vectorIndex ?? 0, - $vectorLength, - waterfalls, - ) - }, - ) - - // Remove amendment values. - $valuesByCalculationNameByVariableNameArray = - $valuesByCalculationNameByVariableNameArray.map( - (valuesByCalculationNameByVariableName) => - Object.fromEntries( - Object.entries(valuesByCalculationNameByVariableName).map( - ([variableName, valuesByCalculationName]) => { - const updatedValuesByCalculationName = { - ...valuesByCalculationName, - } - delete updatedValuesByCalculationName["amendment"] - return [variableName, updatedValuesByCalculationName] - }, - ), - ), - ) - } else { - const calculation: Calculation = (newCalculationByName.amendment = { - running: true, - }) - let situation: SituationWithAxes - if (amendmentSituationIndex === null) { - situation = aggregatedSituation - } else { - calculation.situationIndex = amendmentSituationIndex - situation = cleanSituation($testCases[amendmentSituationIndex], $axes) - } - calculation.input = { - ...message, - parametric_reform: $parametricReform, - reform: reformName ?? revaluationName, // Will be removed when reformName and revaluationName are undefined. - situation, - variables: [ - ...summaryCalculatedVariablesName, - ...otherCalculatedVariablesName, - ...(nonVirtualVariablesNameByReformName[reformName as string] ?? - nonVirtualVariablesNameByReformName[revaluationName as string] ?? - nonVirtualVariablesName), - ], - } - sendTestCasesSimulation("amendment", calculation.input) // Don't wait for result. - } - } - - $calculationByName = newCalculationByName -} - export function isNullVariableValueByCalculationName( variableValueByCalculationName: VariableValueByCalculationName, ): boolean { @@ -318,9 +46,10 @@ export function requestAllBudgetCalculations(variableName: string): void { } } -export function requestAllTestCasesCalculations( +export async function requestAllTestCasesCalculations( requestedSituationIndex: number | null, -): void { +): Promise<void> { + $inspect("1") if ( calculationNames.some((calculationName) => { const situationIndex = @@ -330,6 +59,7 @@ export function requestAllTestCasesCalculations( ) }) ) { + $inspect("2") calculateTestCases( Object.fromEntries( calculationNames.map((calculationName) => { @@ -348,42 +78,31 @@ export function requestAllTestCasesCalculations( } export function requestBudgetCalculation( - requestedCalculations: RequestedCalculations, variableName: string, calculationName: CalculationName, -): RequestedCalculations { +): void { const budgetCalculationNames = variableName === requestedCalculations.budgetVariableName ? (requestedCalculations.budgetCalculationNames ?? new Set<CalculationName>()) : new Set<CalculationName>() if (!budgetCalculationNames.has(calculationName)) { - requestedCalculations = { - ...requestedCalculations, - budgetCalculationNames: budgetCalculationNames.add(calculationName), - budgetVariableName: variableName, - } + requestedCalculations.budgetCalculationNames = + budgetCalculationNames.add(calculationName) + requestedCalculations.budgetVariableName = variableName } - return requestedCalculations } export function requestTestCasesCalculation( - requestedCalculations: RequestedCalculations, calculationName: CalculationName, requestedSituationIndex: number | null, -): RequestedCalculations { +): void { const situationIndex = requestedCalculations.situationIndexByCalculationName[calculationName] if (situationIndex !== null && situationIndex !== requestedSituationIndex) { - requestedCalculations = { - ...requestedCalculations, - situationIndexByCalculationName: { - ...requestedCalculations.situationIndexByCalculationName, - [calculationName]: requestedSituationIndex, - }, - } + requestedCalculations.situationIndexByCalculationName[calculationName] = + requestedSituationIndex } - return requestedCalculations } export function variableValueByCalculationNameFromEvaluation( diff --git a/src/lib/components/BudgetConnexionModal.svelte b/src/lib/components/BudgetConnexionModal.svelte index 7c3f49cfa34f72d636b2e6b22fa6a2c06e5faf4b..ba2e07c9289073a28d73b726a2240d81ac0a3e50 100644 --- a/src/lib/components/BudgetConnexionModal.svelte +++ b/src/lib/components/BudgetConnexionModal.svelte @@ -1,17 +1,13 @@ <script lang="ts"> - import { run } from "svelte/legacy" - import { Dialog } from "bits-ui" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" - import { browser } from "$app/environment" import { goto } from "$app/navigation" import { page } from "$app/stores" import { trackBudgetPublicSimulation, trackBudgetSignInButton, } from "$lib/matomo" + import { shared } from "$lib/shared.svelte" import type { CachedSimulation } from "$lib/simulations" interface Props { @@ -23,12 +19,6 @@ let cachedSimulations: CachedSimulation[] = $state([]) let email = $state("") let emailValid = $state(true) - const requestedSimulationEmail = getContext( - "requestedSimulationEmail", - ) as Writable<string | undefined> - const requestedSimulationSent = getContext( - "requestedSimulationSent", - ) as Writable<boolean> async function fetchCachedSimulations() { const res = await fetch("/budgets/simulations/index", { @@ -47,17 +37,17 @@ if (!emailValid) { return } - $requestedSimulationEmail = email + shared.requestedSimulationEmail = email email = "" } - run(() => { - if (browser && isOpen) { + $effect(() => { + if (isOpen) { fetchCachedSimulations() } }) - run(() => { - if ($requestedSimulationEmail !== undefined) { - $requestedSimulationSent = true + $effect(() => { + if (shared.requestedSimulationEmail !== undefined) { + shared.requestedSimulationSent = true } }) </script> @@ -89,10 +79,10 @@ class="flex items-center gap-2 rounded-md bg-le-bleu px-5 py-2 text-sm font-bold uppercase tracking-[0.085em] text-white shadow-lg hover:bg-blue-800 active:bg-blue-900" onclick={() => { /*login( - $displayMode, - $inputInstantsByVariableNameArray, - $parametricReform, - $testCases, + shared.displayMode, + shared.inputInstantsByVariableNameArray, + shared.parametricReform, + shared.testCases, encodeURIComponent($page.url.toString()), )*/ goto("/auth/prepare?action=login") @@ -136,7 +126,7 @@ la simulation sera rendue publique. Vous serez alors informé par e-mail : </p> - {#if !$requestedSimulationSent} + {#if !shared.requestedSimulationSent} <span class="py-2 pl-10 text-sm font-bold" >Votre adresse e-mail :</span > diff --git a/src/lib/components/BudgetSimulationSharingModal.svelte b/src/lib/components/BudgetSimulationSharingModal.svelte index f192ebaf15f487f78c3a3b1587da49e8a6c8fdcc..5e8018c236dc7636ab89ad85d0ced1f604fb5b59 100644 --- a/src/lib/components/BudgetSimulationSharingModal.svelte +++ b/src/lib/components/BudgetSimulationSharingModal.svelte @@ -1,15 +1,14 @@ <script lang="ts"> + import { Dialog } from "bits-ui" import { run } from "svelte/legacy" - import { Dialog } from "bits-ui" - import { getContext, mount, unmount } from "svelte" - import type { Writable } from "svelte/store" + import { mount, unmount } from "svelte" - import type { BudgetSimulation } from "$lib/budgets" + import { page } from "$app/stores" import CopyClipboard from "$lib/components/CopyClipboard.svelte" import type { DisplayMode } from "$lib/displays" import publicConfig from "$lib/public_config" - import type { ParametricReform } from "$lib/reforms" + import { shared } from "$lib/shared.svelte" import { budgetEditableParametersName } from "$lib/variables" interface Props { @@ -18,25 +17,20 @@ } const { baseUrl } = publicConfig + let { displayMode, isOpen = $bindable(false) }: Props = $props() - const budgetSimulation = getContext("budgetSimulation") as Writable< - BudgetSimulation | undefined - > let clipboardElement: HTMLElement = $state() let hasClickedCopy = $state(false) - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> let isSimulationShared run(() => { - isSimulationShared = $budgetSimulation?.isPublic + isSimulationShared = shared.budgetSimulation?.isPublic }) let url = $derived( new URL( - `/budgets/simulations/${$budgetSimulation?.hash}`, + `/budgets/simulations/${shared.budgetSimulation?.hash}`, baseUrl, ).toString(), ) @@ -77,13 +71,18 @@ } async function onChange() { + if (shared.budgetSimulation === undefined) { + console.error("shared.budgetSimulation is undefined!") + return + } + const urlString = "/budgets/simulations" const res = await fetch(urlString, { body: JSON.stringify({ displayMode, - hash: $budgetSimulation?.hash, + hash: shared.budgetSimulation.hash, parametricReform: Object.fromEntries( - Object.entries($parametricReform).filter(([parameterName]) => + Object.entries(shared.parametricReform).filter(([parameterName]) => budgetEditableParametersName.has(parameterName), ), ), @@ -102,10 +101,7 @@ ) return } - $budgetSimulation = { - ...$budgetSimulation, - isPublic: true, - } + shared.budgetSimulation.isPublic = true } function onClose() { diff --git a/src/lib/components/ModificationsPanel.svelte b/src/lib/components/ModificationsPanel.svelte index 77aa46605ef13befc782a077323346dbc7347d40..a6a02a288461d549fcc6ff88382aa6ba130e7b26 100644 --- a/src/lib/components/ModificationsPanel.svelte +++ b/src/lib/components/ModificationsPanel.svelte @@ -1,13 +1,10 @@ <script lang="ts"> import { ParameterClass, walkParameters } from "@openfisca/json-model" import deepEqual from "deep-equal" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import { requestAllBudgetCalculations, requestAllTestCasesCalculations, - type RequestedCalculations, } from "$lib/calculations.svelte" import PersistentPopover from "$lib/components/PersistentPopover.svelte" import ReformsChanges from "$lib/components/ReformsChanges.svelte" @@ -16,51 +13,26 @@ type DecompositionByName, decompositionCoreByName, decompositionCoreByNameByReformName, - type EvaluationByName, } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" import { rootParameter, rootParameterByReformName } from "$lib/parameters" - import publicConfig from "$lib/public_config" - import type { ParametricReform } from "$lib/reforms" + import { billName, revaluationName, shared } from "$lib/shared.svelte" import { extractInputInstantsFromTestCases, - type Situation, testCasesCore, } from "$lib/situations" import { budgetEditableParametersName, budgetVariablesName, - type ValuesByCalculationNameByVariableName, } from "$lib/variables" interface Props { displayMode: DisplayMode } - const { reformName, revaluationName } = publicConfig let { displayMode }: Props = $props() - const decompositionByName = getContext( - "decompositionByName", - ) as Writable<DecompositionByName> - const evaluationByNameArray = getContext("evaluationByNameArray") as Writable< - EvaluationByName[] - > - const inputInstantsByVariableNameArray = getContext( - "inputInstantsByVariableNameArray", - ) as Writable< - Array<{ - [name: string]: Set<string> - }> - > let isUserModificationsOpen = $state(false) - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const testCases = getContext("testCases") as Writable<Situation[]> - const valuesByCalculationNameByVariableNameArray = getContext( - "valuesByCalculationNameByVariableNameArray", - ) as Writable<ValuesByCalculationNameByVariableName[]> function latestValue(values) { return Object.entries(values ?? {}).sort(([instant1], [instant2]) => @@ -71,19 +43,20 @@ function reset(): void { isUserModificationsOpen = false - $testCases = structuredClone(testCasesCore) - $decompositionByName = buildDecompositionByNameFromCore( - reformName === undefined + shared.testCases = structuredClone(testCasesCore) + shared.decompositionByName = buildDecompositionByNameFromCore( + billName === undefined ? decompositionCoreByName - : (decompositionCoreByNameByReformName[reformName] ?? + : (decompositionCoreByNameByReformName[billName] ?? decompositionCoreByName), ) as DecompositionByName - $evaluationByNameArray = new Array($testCases.length).fill({}) - $inputInstantsByVariableNameArray = - extractInputInstantsFromTestCases($testCases) - $parametricReform = {} - $valuesByCalculationNameByVariableNameArray = new Array( - $testCases.length, + shared.evaluationByNameArray = new Array(shared.testCases.length).fill({}) + shared.inputInstantsByVariableNameArray = extractInputInstantsFromTestCases( + shared.testCases, + ) + shared.parametricReform = {} + shared.valuesByCalculationNameByVariableNameArray = new Array( + shared.testCases.length, ).fill({}) if ( @@ -139,9 +112,9 @@ ), ) let billParametersFiltered = $derived( - reformName === undefined + billName === undefined ? [] - : [...walkParameters(rootParameterByReformName[reformName])].filter( + : [...walkParameters(rootParameterByReformName[billName])].filter( (parameter) => { if (parameter.name?.startsWith("inflateurs")) { return false @@ -165,7 +138,7 @@ ) let billModificationsCount = $derived(billParametersFiltered.length) let modificationsAmendmentCount = $derived( - Object.keys($parametricReform).length, + Object.keys(shared.parametricReform).length, ) let modificationsCount = $derived( modificationsAmendmentCount + billModificationsCount, @@ -175,7 +148,7 @@ ) let showBudgetParametersError = $derived( displayMode.budget && - Object.keys($parametricReform).some( + Object.keys(shared.parametricReform).some( (parameterName) => !budgetEditableParametersName.has(parameterName), ), ) diff --git a/src/lib/components/NavBar.svelte b/src/lib/components/NavBar.svelte index e90de7f269b586bd573be6ad4a4e76e5d5453061..648d62bd41bbc04e88e08f1df6710200252cc6b8 100644 --- a/src/lib/components/NavBar.svelte +++ b/src/lib/components/NavBar.svelte @@ -1,15 +1,12 @@ <script lang="ts"> - import { run } from "svelte/legacy" - // import { // Menu, // MenuButton, // MenuItem, // MenuItems, // } from "@rgossiaux/svelte-headlessui" + import { DropdownMenu } from "bits-ui" import type { SearchResult } from "minisearch" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import { fade } from "svelte/transition" import { browser } from "$app/environment" @@ -19,16 +16,13 @@ import { withLinkedVariableNames } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" import { trackSearchVariable } from "$lib/matomo" - import type { NavbarConfig } from "$lib/navbar" import publicConfig from "$lib/public_config" import WithLinkedVariablesSearchWorker from "$lib/search/search_worker_variables_with_linked?worker" + import { billActive, shared, yearPLF } from "$lib/shared.svelte" import { newSimulationUrl } from "$lib/urls" const { portalUrl } = publicConfig - const billActive = getContext("billActive") as Writable<boolean> - const displayMode = getContext("displayMode") as Writable< - DisplayMode | undefined - > + const dispositifsTypes = [ { title: "Impôt sur le revenu", @@ -72,20 +66,13 @@ parametersVariableName: "exoneration_cotisations_employeur_tode", }, ] - const searchActive = getContext("searchActive") as Writable<boolean> let isSearchInProgress = false let focused = $state(false) let pendingQuery: string | null = null let preventBlur = $state(false) - const searchVariableName = getContext("searchVariableName") as Writable< - string | undefined - > let searchQuery = $state("") let searchResults: SearchResult[] = $state([]) let searchWorker: Worker | undefined = undefined - const showTutorial = getContext("showTutorial") as Writable<boolean> - const navbarConfig = getContext("navbarConfig") as Writable<NavbarConfig> - const yearPLF = getContext("yearPLF") as Writable<number> if (browser) { searchWorker = new WithLinkedVariablesSearchWorker() @@ -108,18 +95,18 @@ function help() { if ($page.route.id === "/") { if ( - $displayMode?.parametersVariableName === undefined || - ($displayMode?.testCasesIndex.length ?? 0) === 0 + shared.displayMode?.parametersVariableName === undefined || + (shared.displayMode?.testCasesIndex.length ?? 0) === 0 ) { goto( newSimulationUrl({ - ...$displayMode, + ...shared.displayMode, parametersVariableName: - $displayMode?.parametersVariableName ?? "irpp_economique", + shared.displayMode?.parametersVariableName ?? "irpp_economique", testCasesIndex: - ($displayMode?.testCasesIndex.length ?? 0) === 0 + (shared.displayMode?.testCasesIndex.length ?? 0) === 0 ? [0] - : $displayMode?.testCasesIndex, + : shared.displayMode?.testCasesIndex, } as DisplayMode), ) } @@ -131,7 +118,7 @@ } as DisplayMode), ) } - $showTutorial = true + shared.showTutorial = true localStorage.removeItem("hideTutorial") } @@ -152,19 +139,22 @@ let { authenticationEnabled, user } = $derived(data) let loginUrl = $derived(`/auth/prepare?action=login`) let logoutUrl = $derived(`/auth/prepare?action=logout`) - run(() => { - if ($searchActive) { + $effect(() => { + if (shared.searchActive) { search(searchQuery) } else { searchResults = [] } }) + + let isAccueilDropdownOpen = $state(false) + let isTutorielDropdownOpen = $state(false) </script> -<!--class:relative={$navbarConfig.position === "relative"}--> +<!--class:relative={shared.navbarConfig.position === "relative"}--> <nav class="z-50 w-full md:top-0" - class:fixed={$navbarConfig.position === "fixed"} + class:fixed={shared.navbarConfig.position === "fixed"} > <div class="mx-auto bg-le-jaune-very-dark px-2 shadow-md md:h-12 md:px-3 2xl:h-14" @@ -182,15 +172,15 @@ alt="Logo de l'Assemblée nationale" /> - <!-- Tutoriel Dropdown --> - <!-- <Menu let:open> - <MenuButton + <!-- Accueil Dropdown --> + <DropdownMenu.Root bind:open={isAccueilDropdownOpen}> + <DropdownMenu.Trigger class="cursor-pointer overflow-hidden rounded-lg text-sm uppercase text-white hover:bg-gray-400 hover:bg-opacity-20 focus:outline-none active:bg-gray-400 active:bg-opacity-40" > <div class="py-2 pl-2 pr-3 lg:pl-4" - class:bg-gray-400={open} - class:bg-opacity-40={open} + class:bg-gray-400={isAccueilDropdownOpen} + class:bg-opacity-40={isAccueilDropdownOpen} > <span class="flex items-center gap-1.5"> <span class="flex-col items-center"> @@ -201,39 +191,48 @@ </span><iconify-icon class="mr-1 flex align-[-0.2rem] text-lg lg:hidden" icon="ri:home-2-fill" - /> - <iconify-icon class="text-2xl" icon="ri:arrow-down-s-line" /> + ></iconify-icon> + <iconify-icon class="text-2xl" icon="ri:arrow-down-s-line" + ></iconify-icon> </span> </div> - </MenuButton> - <MenuItems - class="absolute left-5 top-0 z-50 mr-6 mt-14 w-64 rounded bg-white text-black shadow-xl ring-1 ring-black ring-opacity-5 focus:outline-none" + </DropdownMenu.Trigger> + <DropdownMenu.Content + class="absolute z-50 mr-6 mt-2 w-64 -translate-x-1/2 rounded bg-white text-black shadow-xl ring-1 ring-black ring-opacity-5 focus:outline-none" > - <MenuItem - as="a" - class="block cursor-pointer border-b px-4 py-3 text-center text-sm uppercase tracking-wider hover:bg-gray-100" - href="/accueil" - > - Accueil simulateur - </MenuItem> + <DropdownMenu.Item> + {#snippet child({ props })} + <a + class="block cursor-pointer border-b px-4 py-3 text-center text-sm uppercase tracking-wider hover:bg-gray-100" + href="/accueil" + {...props} + > + Accueil simulateur + </a> + {/snippet} + </DropdownMenu.Item> - <MenuItem - as="a" - class="block cursor-pointer border-b px-4 py-3 text-center hover:bg-gray-100" - href={portalUrl} - > - <iconify-icon - class="mr-1 align-[-0.2rem] text-lg" - icon="ri:home-2-line" - />Accueil LexImpact<iconify-icon - class="align-[-0.25rem] text-xl" - icon="ri:arrow-right-up-line" - /> - </MenuItem> - </MenuItems> - </Menu> --> + <DropdownMenu.Item> + {#snippet child({ props })} + <a + class="block cursor-pointer border-b px-4 py-3 text-center hover:bg-gray-100" + href={portalUrl} + {...props} + > + <iconify-icon + class="mr-1 align-[-0.2rem] text-lg" + icon="ri:home-2-line" + ></iconify-icon>Accueil LexImpact<iconify-icon + class="align-[-0.25rem] text-xl" + icon="ri:arrow-right-up-line" + ></iconify-icon> + </a> + {/snippet} + </DropdownMenu.Item> + </DropdownMenu.Content> + </DropdownMenu.Root> </div> - {#if $navbarConfig.showSearch} + {#if shared.navbarConfig.showSearch} <!-- Logo --> <a href="/" @@ -255,12 +254,12 @@ Socio-Fiscal </span> </div> - {#if $billActive} + {#if billActive} <div class="m-1 flex -rotate-6 flex-col rounded-sm bg-white p-0.5 px-1 pt-0 text-[0.62rem] text-le-gris-dispositif-dark shadow-lg" > <span class="leading-4">Prévisions</span><span - class="text-[1.2rem] font-bold leading-4">{$yearPLF}</span + class="text-[1.2rem] font-bold leading-4">{yearPLF}</span > </div> {/if} @@ -268,14 +267,14 @@ {/if} </div> - {#if $navbarConfig.showSearch} + {#if shared.navbarConfig.showSearch} <div in:fade={{ duration: 100, delay: 100 }} out:fade={{ duration: 100 }} > <!-- Bloc centre pour la barre de recherche --> <NavBarSearch - bind:active={$searchActive} + bind:active={shared.searchActive} bind:query={searchQuery} on:blur={() => { if (!preventBlur) { @@ -287,7 +286,7 @@ </div> {/if} - {#if !$navbarConfig.showSearch} + {#if !shared.navbarConfig.showSearch} <div class="pointer-events-none absolute inset-0 mx-auto flex h-full items-center justify-center gap-2 uppercase text-white" in:fade={{ duration: 100, delay: 150 }} @@ -312,83 +311,101 @@ <!-- Bloc droite pour les commandes avancées--> <ul class="flex basis-1/3 items-center justify-end gap-2 pr-2 xl:gap-5"> <!-- Tutoriel Dropdown --> - <!-- <Menu let:open> - <MenuButton + <DropdownMenu.Root bind:open={isTutorielDropdownOpen}> + <DropdownMenu.Trigger class="cursor-pointer overflow-hidden rounded-lg text-sm uppercase text-white hover:bg-gray-400 hover:bg-opacity-20 focus:outline-none active:bg-gray-400 active:bg-opacity-40" > <div class="py-2 pl-4 pr-3" - class:bg-gray-400={open} - class:bg-opacity-40={open} + class:bg-gray-400={isTutorielDropdownOpen} + class:bg-opacity-40={isTutorielDropdownOpen} > <span class="flex items-center gap-1.5"> <iconify-icon class="align-[-0.3rem] text-xl" icon="ri-question-fill" - /> <span class="hidden xl:inline-flex">Tutoriel</span> - <iconify-icon class="text-2xl" icon="ri:arrow-down-s-line" /> + ></iconify-icon> + <span class="hidden xl:inline-flex">Tutoriel</span> + <iconify-icon class="text-2xl" icon="ri:arrow-down-s-line" + ></iconify-icon> </span> </div> - </MenuButton> - <MenuItems - class="absolute right-0 top-0 z-50 mr-6 mt-14 w-64 rounded bg-white text-black shadow-xl ring-1 ring-black ring-opacity-5 focus:outline-none" + </DropdownMenu.Trigger> + <DropdownMenu.Content + class="absolute z-50 mr-6 mt-2 w-64 -translate-x-1/2 rounded bg-white text-black shadow-xl ring-1 ring-black ring-opacity-5 focus:outline-none" > - <MenuItem - as="a" - class="block cursor-pointer border-b px-4 py-3 hover:bg-gray-100" - href="/accueil#impacts-budgetaires" - > - <iconify-icon - class="mr-1 align-[-0.25rem] text-xl" - icon="ri-list-check-3" - /> Présentation des fonctionnalités - </MenuItem> - <MenuItem - as="a" - class="group hidden cursor-pointer border-b px-4 py-3 hover:bg-gray-100 md:block" - on:click={help} - > - <iconify-icon - class="mr-1 align-[-0.25rem] text-xl" - icon="ri:drag-drop-line" - />Tutoriel interactif - <br /><span - class="lx-link-uppercase hidden group-hover:flex group-hover:underline" - >commencer<iconify-icon - class="mr-1 align-[-0.25rem] text-xl" - icon="ri:arrow-right-line" - /></span - > - </MenuItem> - <MenuItem - as="a" - class="block cursor-pointer border-b px-4 py-3 hover:bg-gray-100" - aria-label="Tutoriels vidéos sur le site Dailymotion" - href="https://www.dailymotion.com/leximpact.an.fr" - target="_blank" - > - <iconify-icon - class="mr-1 align-[-0.25rem] text-xl" - icon="ri:video-line" - /> - Tutoriels vidéos - <iconify-icon - class="ml-0.5 align-[-0.15rem] text-base" - icon="ri:external-link-line" - /> - </MenuItem> - <MenuItem - as="a" - class="block cursor-pointer border-b px-4 py-3 hover:bg-gray-100" - href="/fonctionnement" - > - <iconify-icon - class="mr-1 align-[-0.25rem] text-xl" - icon="ri-database-line" - />Méthode de calcul et traitement des données - </MenuItem> - </MenuItems> - </Menu> --> + <DropdownMenu.Item> + {#snippet child({ props })} + <a + class="block cursor-pointer border-b px-4 py-3 hover:bg-gray-100" + href="/accueil#impacts-budgetaires" + {...props} + > + <iconify-icon + class="mr-1 align-[-0.25rem] text-xl" + icon="ri-list-check-3" + ></iconify-icon> Présentation des fonctionnalités + </a> + {/snippet} + </DropdownMenu.Item> + <DropdownMenu.Item> + {#snippet child({ props })} + <button + class="group hidden w-full cursor-pointer border-b px-4 py-3 text-start hover:bg-gray-100 md:block" + onclick={help} + {...props} + > + <iconify-icon + class="mr-1 align-[-0.25rem] text-xl" + icon="ri:drag-drop-line" + />Tutoriel interactif + <br /><span + class="lx-link-uppercase hidden group-hover:flex group-hover:underline" + >commencer<iconify-icon + class="mr-1 align-[-0.25rem] text-xl" + icon="ri:arrow-right-line" + /></span + > + </button> + {/snippet} + </DropdownMenu.Item> + <DropdownMenu.Item> + {#snippet child({ props })} + <a + class="block cursor-pointer border-b px-4 py-3 hover:bg-gray-100" + aria-label="Tutoriels vidéos sur le site Dailymotion" + href="https://www.dailymotion.com/leximpact.an.fr" + target="_blank" + {...props} + > + <iconify-icon + class="mr-1 align-[-0.25rem] text-xl" + icon="ri:video-line" + /> + Tutoriels vidéos + <iconify-icon + class="ml-0.5 align-[-0.15rem] text-base" + icon="ri:external-link-line" + /> + </a> + {/snippet} + </DropdownMenu.Item> + <DropdownMenu.Item> + {#snippet child({ props })} + <a + class="block cursor-pointer border-b px-4 py-3 hover:bg-gray-100" + href="/fonctionnement" + {...props} + > + <iconify-icon + class="mr-1 align-[-0.25rem] text-xl" + icon="ri-database-line" + />Méthode de calcul et traitement des données + </a> + {/snippet} + </DropdownMenu.Item> + </DropdownMenu.Content> + </DropdownMenu.Root> <!-- Bouton se connecter --> @@ -471,12 +488,12 @@ <span class="font-light leading-4">LexImpact</span> <span class="text-lg leading-5">Socio-Fiscal</span> </div> - {#if $billActive} + {#if billActive} <div class="m-1 flex -rotate-6 flex-col rounded-sm bg-white p-0.5 px-1 py-0 text-[0.62rem] text-le-gris-dispositif-dark shadow-lg" > <span class="leading-4">Prévisions</span><span - class="text-[1.2rem] font-bold leading-4">{$yearPLF}</span + class="text-[1.2rem] font-bold leading-4">{yearPLF}</span > </div> {/if} @@ -606,7 +623,7 @@ </div> <NavBarSearch - bind:active={$searchActive} + bind:active={shared.searchActive} bind:query={searchQuery} on:blur={() => { if (!preventBlur) { @@ -618,14 +635,14 @@ </div> </div> - {#if focused || $searchActive} + {#if focused || shared.searchActive} <div class="absolute inset-0 -z-10 bg-[rgba(0,0,0,.3)] transition-all" ></div> <div class="absolute top-24 w-full overflow-hidden rounded-b-lg border bg-white shadow-lg md:left-[calc((100%-350px)/2)] md:top-12 md:w-[350px] lg:left-[calc((100%-500px)/2)] lg:w-[500px] 2xl:left-[calc((100%-600px)/2)] 2xl:top-14 2xl:w-[600px]" > - {#if $searchActive} + {#if shared.searchActive} <ul class="max-h-56 list-none overflow-y-auto py-2 md:max-h-[80vh]"> {#if searchResults.length > 0} {#each searchResults as variable, index (`found_variable_$${index}`)} @@ -639,7 +656,7 @@ searchResults.length, ) searchQuery = "" - $searchVariableName = variable.name + shared.searchVariableName = variable.name }} > <span @@ -670,7 +687,7 @@ <button class="flex w-full items-center gap-3 px-3 py-1 transition hover:bg-gray-200/70 active:bg-gray-200 2xl:py-3" onclick={() => { - $searchVariableName = dispositif.parametersVariableName + shared.searchVariableName = dispositif.parametersVariableName focused = false }} onmousedown={() => (preventBlur = true)} diff --git a/src/lib/components/ReformsChanges.svelte b/src/lib/components/ReformsChanges.svelte index c8f5d9a514cf33547983bef448a013ba25cde861..179d9a64faf97d52e6e1b01be5afc617f6b70e75 100644 --- a/src/lib/components/ReformsChanges.svelte +++ b/src/lib/components/ReformsChanges.svelte @@ -10,11 +10,8 @@ ParameterClass, type Variable, } from "@openfisca/json-model" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import Tooltip from "$lib/components/Tooltip.svelte" - import type { DecompositionByName } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" import { asValueParameter, @@ -22,11 +19,15 @@ rootParameter, rootParameterByReformName, } from "$lib/parameters" - import publicConfig from "$lib/public_config" + import { ParameterReformChangeType } from "$lib/reforms" import { - ParameterReformChangeType, - type ParametricReform, - } from "$lib/reforms" + billActive, + billName, + budgetDate, + revaluationName, + shared, + yearPLF, + } from "$lib/shared.svelte" import { newSimulationUrl } from "$lib/urls" import { formatValue } from "$lib/values" import { @@ -41,24 +42,14 @@ revaluationParametersFiltered: Parameter[] } - const { reformName, revaluationName } = publicConfig let { billParametersFiltered, displayMode, revaluationParametersFiltered, }: Props = $props() - const billActive = getContext("billActive") as Writable<boolean> let billParametersListOpen = $state(false) - const budgetDate = getContext("budgetDate") as Writable<string> - const decompositionByName = getContext( - "decompositionByName", - ) as Writable<DecompositionByName> - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> let revaluationParametersListOpen = $state(false) - const yearPLF = getContext("yearPLF") as Writable<number> function buildVariableByParameter(parameterById: { [id: string]: Parameter @@ -108,13 +99,13 @@ : undefined } // $: billChanges = - // reformName === undefined ? undefined : reformChangesByName[reformName] + // billName === undefined ? undefined : reformChangesByName[$billName] // Note: A reform parameters tree is always more complete than a parameters tree before reform. // And the children of a reform node parameter always contain the children of the node parameter // before reform (albeit with some different value parameters). let billRootParameter = $derived( - rootParameterByReformName[reformName] ?? rootParameter, + rootParameterByReformName[billName] ?? rootParameter, ) // function* walkParametersChangeName(parameterChange, ids: string[] = []) { // if (Object.keys(parameterChange).length === 1) { @@ -141,11 +132,11 @@ // Note: A reform variable is always more complete than a variable before reform. // But it may contain different formulas, with different parameters & variables. let billVariableSummaryByName = $derived( - variableSummaryByNameByReformName[reformName] ?? variableSummaryByName, + variableSummaryByNameByReformName[billName] ?? variableSummaryByName, ) let decompositionVariableSummaryByName = $derived( Object.fromEntries( - Object.keys($decompositionByName).reduce( + Object.keys(shared.decompositionByName).reduce( (arr: [string, Variable][], variableName: string) => { const variableSummary: Variable | undefined = billVariableSummaryByName[variableName] @@ -163,7 +154,7 @@ // Object.entries(decompositionVariableSummaryByName) // .map(([variableName, variableSummary]) => { // const parametersName = new Set( - // iterVariableParametersName(variableSummary, $budgetDate), + // iterVariableParametersName(variableSummary, budgetDate), // ) // const rootParameterById = mergeParameters( // [...parametersName].map((name) => @@ -188,7 +179,7 @@ Object.entries(decompositionVariableSummaryByName) .map(([variableName, variableSummary]) => { const directParametersName = new Set( - getVariableFormula(variableSummary, $budgetDate)?.parameters ?? [], + getVariableFormula(variableSummary, budgetDate)?.parameters ?? [], ) const rootDirectParameterById = mergeParameters( [...directParametersName].map((name) => @@ -245,12 +236,12 @@ ) </script> -{#if $billActive} +{#if billActive} <div class="p-2 lg:px-4"> {#if revaluationParametersFiltered.length !== 0} <div class="mb-4"> <h4 class="text-sm font-bold text-le-gris-dispositif-dark lg:text-sm"> - Droit attendu en {$yearPLF} : + Droit attendu en {yearPLF} : </h4> <button class="mt-1 text-left text-xs text-le-gris-dispositif-dark" @@ -379,7 +370,7 @@ {/if} <h4 class="text-sm font-bold text-le-rouge-bill lg:text-sm"> - PLF et PLFSS {$yearPLF} : + PLF et PLFSS {yearPLF} : </h4> <button @@ -485,9 +476,9 @@ {parameter?.titles?.filter(Boolean).join(" > ")} <br /><br /> <span> - Indexation d'usage par le PLF {$yearPLF} à {getInflatorLatestValueFormatted( + Indexation d'usage par le PLF {yearPLF} à {getInflatorLatestValueFormatted( parameter, - reformName, + billName, )} </span> </div> @@ -503,7 +494,7 @@ ></div> {/if} <!-- <h5 class="text-sm"> - {reformMetadataByName[reformName].label} : + {reformMetadataByName[billName].label} : </h5> <ul class="list-inside"> {#each Object.values(billChanges?.editable_processed_parameters ?? {}) as rootParameterChange} @@ -538,7 +529,7 @@ Votre <span class="bg-le-jaune">réforme :</span> </h4> - {#if Object.keys($parametricReform).length === 0} + {#if Object.keys(shared.parametricReform).length === 0} <p class="my-3 bg-gray-200 text-center text-xs italic leading-relaxed text-gray-600 lg:text-sm" > @@ -546,7 +537,7 @@ </p> {:else} <ul class="list-inside"> - {#each Object.entries($parametricReform) as [parameterName, parameterReform]} + {#each Object.entries(shared.parametricReform) as [parameterName, parameterReform]} {@const parameter = getParameter(billRootParameter, parameterName)} {@const tooltipId = `modification-reforme-${parameterName.replace( /\./g, diff --git a/src/lib/components/TestComponent.svelte b/src/lib/components/TestComponent.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7dfcd36cdb64553693558a1d9a22c4eef77a9479 --- /dev/null +++ b/src/lib/components/TestComponent.svelte @@ -0,0 +1,14 @@ +<script lang="ts"> + let { variable } = $props() + + const yFromQuantile = (_variableA: string = variable.a) => { + return _variableA + } + + const arr = [0, 1, 2] +</script> + +{#each arr as item} + {@const val = yFromQuantile()} + {val} +{/each} diff --git a/src/lib/components/ValueChange.svelte b/src/lib/components/ValueChange.svelte index 28f49c4f0366ec804399f4dc31222a88dfc40f45..c5ab00eb2401a48fb845cb4948dc1301bc1f2f25 100644 --- a/src/lib/components/ValueChange.svelte +++ b/src/lib/components/ValueChange.svelte @@ -1,9 +1,5 @@ <script lang="ts"> - import { getContext } from "svelte" - import type { Writable } from "svelte/store" - - import type { ParametricReform } from "$lib/reforms" - import { type CalculationByName } from "$lib/situations" + import { billActive, shared, yearPLF } from "$lib/shared.svelte" import { valueFormatter } from "$lib/values" import type { VariableValueByCalculationName } from "$lib/variables" @@ -27,15 +23,6 @@ valueByCalculationName, }: Props = $props() - const billActive = getContext("billActive") as Writable<boolean> - const calculationByName = getContext( - "calculationByName", - ) as Writable<CalculationByName> - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const yearPLF = getContext("yearPLF") as Writable<number> - function mustShowAmendmentValue( amendmentValueFormatted: string | undefined, billValueFormatted: string | undefined, @@ -72,12 +59,12 @@ ) let format = $derived(valueFormatter(baseValue, unitName, compact)) let runningCalculationNames = $derived( - Object.entries($calculationByName) + Object.entries(shared.calculationByName) .filter(([, calculation]) => calculation.running) .map(([calculationName]) => calculationName), ) let modificationsAmendmentCount = $derived( - Object.keys($parametricReform).length, + Object.keys(shared.parametricReform).length, ) let amendmentValueFormatted = $derived( amendmentValue === undefined ? undefined : format(amendmentValue), @@ -125,11 +112,11 @@ class="w-24 text-xs font-normal underline decoration-dotted" class:block={!inline} title={billValue - ? `Droit attendu en ${$yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS ${$yearPLF}.` - : `Droit en vigueur en ${$yearPLF - 1}`} + ? `Droit attendu en ${yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS ${yearPLF}.` + : `Droit en vigueur en ${yearPLF - 1}`} > - {#if $billActive} - Droit {$yearPLF} <br />sans PLF/PLSS + {#if billActive} + Droit {yearPLF} <br />sans PLF/PLSS {:else} Droit en vigueur {/if} @@ -153,11 +140,11 @@ class="w-24 text-xs font-normal underline decoration-dotted" class:block={!inline} title={billValue - ? `Droit attendu en ${$yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS ${$yearPLF}.` - : `Droit en vigueur en ${$yearPLF - 1}`} + ? `Droit attendu en ${yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS ${yearPLF}.` + : `Droit en vigueur en ${yearPLF - 1}`} > - {#if $billActive} - Droit {$yearPLF} <br />sans PLF/PLSS + {#if billActive} + Droit {yearPLF} <br />sans PLF/PLSS {:else} Droit en vigueur {/if} @@ -178,7 +165,7 @@ class="text-xs font-normal text-le-rouge-bill" class:block={!inline} > - PLF/PLFSS {$yearPLF} + PLF/PLFSS {yearPLF} <br /> </span> {/if} @@ -197,7 +184,7 @@ class="text-xs font-normal text-le-rouge-bill" class:block={!inline} > - PLF/PLFSS {$yearPLF} + PLF/PLFSS {yearPLF} <br /> </span> {/if} diff --git a/src/lib/components/ValueChangeCompare.svelte b/src/lib/components/ValueChangeCompare.svelte index f7e305282d9456b1f398eacef2273d4ed495ffbb..c8f6d45ee949e40e3dd015af0ab3ed6faeb896df 100644 --- a/src/lib/components/ValueChangeCompare.svelte +++ b/src/lib/components/ValueChangeCompare.svelte @@ -1,9 +1,5 @@ <script lang="ts"> - import { getContext } from "svelte" - import type { Writable } from "svelte/store" - - import type { ParametricReform } from "$lib/reforms" - import type { CalculationByName } from "$lib/situations" + import { billActive, shared, yearPLF } from "$lib/shared.svelte" import { valueFormatter } from "$lib/values" import type { VariableValueByCalculationName } from "$lib/variables" @@ -23,15 +19,6 @@ valueByCalculationName1, }: Props = $props() - const billActive = getContext("billActive") as Writable<boolean> - const calculationByName = getContext( - "calculationByName", - ) as Writable<CalculationByName> - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const yearPLF = getContext("yearPLF") as Writable<number> - function mustShowAmendmentValue( amendmentValueFormatted: string | undefined, billValueFormatted: string | undefined, @@ -147,12 +134,12 @@ let showLawValue0 = $derived(lawValue0Formatted !== undefined) let showLawValue1 = $derived(lawValue1Formatted !== undefined) let runningCalculationNames = $derived( - Object.entries($calculationByName) + Object.entries(shared.calculationByName) .filter(([, calculation]) => calculation.running) .map(([calculationName]) => calculationName), ) let modificationsAmendmentCount = $derived( - Object.keys($parametricReform).length, + Object.keys(shared.parametricReform).length, ) </script> @@ -165,10 +152,10 @@ {#if legend} <div class="ml-1 text-xs font-normal underline decoration-dotted" - title="Droit attendu en {$yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {$yearPLF}" + title="Droit attendu en {yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {yearPLF}" > - {#if $billActive} - Droit {$yearPLF} <br />sans PLF/PLSS + {#if billActive} + Droit {yearPLF} <br />sans PLF/PLSS {:else} Droit en vigueur {/if} @@ -192,10 +179,10 @@ {#if legend} <div class="ml-1 text-xs font-normal underline decoration-dotted" - title="Droit attendu en {$yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {$yearPLF}" + title="Droit attendu en {yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {yearPLF}" > - {#if $billActive} - Droit {$yearPLF} <br />sans PLF/PLSS + {#if billActive} + Droit {yearPLF} <br />sans PLF/PLSS {:else} Droit en vigueur {/if} @@ -219,10 +206,10 @@ {#if legend} <div class="ml-1 self-center text-xs font-normal text-gray-500 underline decoration-dotted" - title="Droit attendu en {$yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {$yearPLF}." + title="Droit attendu en {yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {yearPLF}." > - {#if $billActive} - Droit {$yearPLF} <br />sans PLF/PLSS + {#if billActive} + Droit {yearPLF} <br />sans PLF/PLSS {:else} Droit en vigueur {/if} @@ -244,10 +231,10 @@ {#if legend} <div class="ml-1 self-center text-xs font-normal text-gray-500 underline decoration-dotted" - title="Droit attendu en {$yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {$yearPLF}." + title="Droit attendu en {yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {yearPLF}." > - {#if $billActive} - Droit {$yearPLF} <br />sans PLF/PLSS + {#if billActive} + Droit {yearPLF} <br />sans PLF/PLSS {:else} Droit en vigueur {/if} @@ -265,7 +252,7 @@ </div> {#if legend} <div class="ml-1 text-xs font-normal text-le-rouge-bill"> - PLF/PLFSS {$yearPLF} + PLF/PLFSS {yearPLF} <br /> </div> {/if} @@ -283,7 +270,7 @@ </div> {#if legend} <div class="ml-1 text-xs font-normal text-le-rouge-bill"> - PLF/PLFSS {$yearPLF} + PLF/PLFSS {yearPLF} <br /> </div> {/if} @@ -301,7 +288,7 @@ <div class="ml-1 self-center text-xs font-normal text-le-rouge-bill" > - PLF/PLFSS {$yearPLF} + PLF/PLFSS {yearPLF} <br /> </div> {/if} @@ -317,7 +304,7 @@ <div class="ml-1 self-center text-xs font-normal text-le-rouge-bill" > - PLF/PLFSS {$yearPLF} + PLF/PLFSS {yearPLF} <br /> </div> {/if} diff --git a/src/lib/components/ValueChangeGagnantsPerdants.svelte b/src/lib/components/ValueChangeGagnantsPerdants.svelte index 634f513ddb8f3c4427f7a8ab5e194454c781a931..e0f64744d5577df213d5136fee2184cc165f5bd2 100644 --- a/src/lib/components/ValueChangeGagnantsPerdants.svelte +++ b/src/lib/components/ValueChangeGagnantsPerdants.svelte @@ -1,13 +1,11 @@ <script lang="ts"> - import { getContext } from "svelte" - import type { Writable } from "svelte/store" - import Tooltip from "$lib/components/Tooltip.svelte" import { valueFormatter } from "$lib/values" import type { BudgetVariableType, VariableValueByCalculationName, } from "$lib/variables" + import { yearPLF } from "$lib/shared.svelte" interface Props { augmentation?: boolean @@ -33,7 +31,6 @@ let entityTypeName = variableType === "prestation" ? "ménage" : "foyer fiscal" let entitiesTypeName = variableType === "prestation" ? "ménages" : "foyers fiscaux" - const yearPLF = getContext("yearPLF") as Writable<number> let amendmentLawValue = $derived( valueByCalculationName.amendment_law as number | undefined | null, @@ -129,7 +126,7 @@ > <span class="text-sm underline decoration-dotted" - >par rapport au droit {$yearPLF} sans PLF</span + >par rapport au droit {yearPLF} sans PLF</span >. <br /> {#if showAmendmentValue} @@ -161,11 +158,11 @@ ></iconify-icon></span > <span class="text-sm underline decoration-dotted" - >par rapport au droit {$yearPLF} sans PLF</span + >par rapport au droit {yearPLF} sans PLF</span >. {:else if showAmendmentValue} <span class="text-sm underline decoration-dotted" - >par rapport au droit {$yearPLF} sans PLF</span + >par rapport au droit {yearPLF} sans PLF</span >. {/if} {:else} @@ -181,7 +178,7 @@ ></iconify-icon></span > <span class="text-sm underline decoration-dotted" - >par rapport au droit en vigueur {$yearPLF}</span + >par rapport au droit en vigueur {yearPLF}</span >. {/if} </p> @@ -212,7 +209,7 @@ {/if} <span class="underline decoration-2 underline-offset-2" - >par rapport au droit {$yearPLF} sans PLF</span + >par rapport au droit {yearPLF} sans PLF</span >. </p> @@ -238,7 +235,7 @@ {#if showAmendmentValue && showBillValue} <span class="underline decoration-le-rouge-bill decoration-2 underline-offset-2" - >par rapport au droit {$yearPLF} avec PLF.</span + >par rapport au droit {yearPLF} avec PLF.</span ><br /> <span class="bg-le-jaune" >{amendmentLawValue === null @@ -257,11 +254,11 @@ un impact nul ou inférieur à 5% {variableLabels.ofThe} {/if} <span class="underline decoration-2 underline-offset-2" - >par rapport au droit {$yearPLF} sans PLF</span + >par rapport au droit {yearPLF} sans PLF</span >. {:else} <span class="underline decoration-2 underline-offset-2" - >par rapport au droit {$yearPLF} sans PLF</span + >par rapport au droit {yearPLF} sans PLF</span >. {/if} {/if} @@ -285,7 +282,7 @@ {/if} <span class="underline decoration-black decoration-2 underline-offset-2" - >par rapport au droit en vigueur {$yearPLF}.</span + >par rapport au droit en vigueur {yearPLF}.</span ><br /> {/if} </div> diff --git a/src/lib/components/ValueChangeGraph.svelte b/src/lib/components/ValueChangeGraph.svelte index 9f54a1f6d641641d2b96ca613eb85fb1153b6acd..be3307134c0d506034afd57a909ab89d8ac713ae 100644 --- a/src/lib/components/ValueChangeGraph.svelte +++ b/src/lib/components/ValueChangeGraph.svelte @@ -1,9 +1,5 @@ <script lang="ts"> - import { getContext } from "svelte" - import type { Writable } from "svelte/store" - - import type { ParametricReform } from "$lib/reforms" - import { type CalculationByName } from "$lib/situations" + import { shared } from "$lib/shared.svelte" import { valueFormatter } from "$lib/values" import type { VariableValueByCalculationName } from "$lib/variables" @@ -21,13 +17,6 @@ children, }: Props = $props() - const calculationByName = getContext( - "calculationByName", - ) as Writable<CalculationByName> - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - function mustShowAmendmentValue( amendmentValueFormatted: string | undefined, billValueFormatted: string | undefined, @@ -64,12 +53,12 @@ ) let format = $derived(valueFormatter(baseValue, unitName)) let runningCalculationNames = $derived( - Object.entries($calculationByName) + Object.entries(shared.calculationByName) .filter(([, calculation]) => calculation.running) .map(([calculationName]) => calculationName), ) let modificationsAmendmentCount = $derived( - Object.keys($parametricReform).length, + Object.keys(shared.parametricReform).length, ) let amendmentValueFormatted = $derived( amendmentValue === undefined ? undefined : format(amendmentValue), diff --git a/src/lib/components/WaterfallCompareView.svelte b/src/lib/components/WaterfallCompareView.svelte index 4802d5f447824a3cae563a2293d675516ccdfcc1..87ccc6d29d76aa9c3011eaed4d5fb6912fade49b 100644 --- a/src/lib/components/WaterfallCompareView.svelte +++ b/src/lib/components/WaterfallCompareView.svelte @@ -1,28 +1,21 @@ <script lang="ts"> import { preventDefault } from "svelte/legacy" - import type { VariableByName, Waterfall } from "@openfisca/json-model" - import { createEventDispatcher, getContext } from "svelte" - import type { Writable } from "svelte/store" + import type { VariableByName } from "@openfisca/json-model" import { goto } from "$app/navigation" import { page } from "$app/stores" import Tooltip from "$lib/components/Tooltip.svelte" - import type { - DecompositionByName, - EvaluationByName, - } from "$lib/decompositions" + import type { EvaluationByName } from "$lib/decompositions" import { buildVisibleDecompositionsForComparison } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" import { entityByKey } from "$lib/entities" - import publicConfig from "$lib/public_config" - import type { ParametricReform } from "$lib/reforms" - import type { CalculationByName, Situation } from "$lib/situations" + import { shared } from "$lib/shared.svelte" + import type { Situation } from "$lib/situations" import { newSimulationUrl } from "$lib/urls" import { removeNegativeZero } from "$lib/values" interface Props { - decompositionByName: DecompositionByName displayMode: DisplayMode evaluationByNameArray: EvaluationByName[] situations: Situation[] @@ -32,9 +25,7 @@ year: number } - const { revaluationName } = publicConfig let { - decompositionByName = $bindable(), displayMode, evaluationByNameArray, situations, @@ -44,9 +35,6 @@ year, }: Props = $props() - const calculationByName = getContext( - "calculationByName", - ) as Writable<CalculationByName> const dateFormatter = new Intl.DateTimeFormat("fr-FR", { dateStyle: "full" }) .format const deltaFormatter = (value: number): string => @@ -57,33 +45,28 @@ signDisplay: "never", style: "currency", }).format(removeNegativeZero(value)) - const dispatch = createEventDispatcher() - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const showNulls = getContext("showNulls") as Writable<boolean> - const vectorLength = getContext("vectorLength") as Writable<number> - const waterfall = getContext("waterfall") as Writable<Waterfall> - let useRevaluationInsteadOfLaw = revaluationName !== undefined + let useRevaluationInsteadOfLaw = $derived( + $page.data.revaluationName !== undefined, + ) let firstCalculationName = $derived( useRevaluationInsteadOfLaw ? "revaluation" : "law", ) let runningCalculationNames = $derived( - Object.entries($calculationByName) + Object.entries(shared.calculationByName) .filter(([, calculation]) => calculation.running) .map(([calculationName]) => calculationName), ) let modificationsAmendmentCount = $derived( - Object.keys($parametricReform).length, + Object.keys(shared.parametricReform).length, ) let visibleDecompositions = $derived( buildVisibleDecompositionsForComparison( - decompositionByName, + shared.decompositionByName, entityByKey, situationsToCompareIndex.map( (situationIndex) => evaluationByNameArray[situationIndex], @@ -92,10 +75,10 @@ (situationIndex) => situations[situationIndex], ), variableSummaryByName, - $waterfall, - $showNulls, + shared.waterfall, + shared.showNulls, useRevaluationInsteadOfLaw, - $vectorLength, + shared.vectorLength, year, ), ) @@ -116,11 +99,10 @@ } let decomposition = visibleDecomposition.decomposition if (!decomposition.open) { - decompositionByName = { - ...decompositionByName, - [decomposition.name]: { ...decomposition, open: true }, + shared.decompositionByName[decomposition.name] = { + ...decomposition, + open: true, } - dispatch("changeDecompositionByName", decompositionByName) return } } @@ -135,11 +117,10 @@ } let decomposition = visibleDecomposition.decomposition if (decomposition.open) { - decompositionByName = { - ...decompositionByName, - [decomposition.name]: { ...decomposition, open: false }, + shared.decompositionByName[decomposition.name] = { + ...decomposition, + open: false, } - dispatch("changeDecompositionByName", decompositionByName) return } } @@ -598,7 +579,7 @@ > <label class="inline-flex text-xs leading-none text-gray-600"> <input - bind:checked={$showNulls} + bind:checked={shared.showNulls} class="checked rounded bg-gray-100 accent-gray-500" type="checkbox" /> diff --git a/src/lib/components/WaterfallPlainView.svelte b/src/lib/components/WaterfallPlainView.svelte index b1a91d3f6aae00180630b2fbbacd06735a19370b..c5f1b4b37975a07028c4692b4a2e69f53ccf817a 100644 --- a/src/lib/components/WaterfallPlainView.svelte +++ b/src/lib/components/WaterfallPlainView.svelte @@ -12,7 +12,7 @@ decompositionCoreByNameByReformName, waterfalls, } from "$lib/decompositions" - import publicConfig from "$lib/public_config" + import { billName } from "$lib/shared.svelte" import { variableSummaryByName, variableSummaryByNameByReformName, @@ -22,7 +22,6 @@ compact?: boolean } - const { reformName } = publicConfig let { compact = false }: Props = $props() const dispatch = createEventDispatcher() @@ -31,9 +30,9 @@ let decompositionByName = $derived( buildDecompositionByNameFromCore( - reformName === undefined + billName === undefined ? decompositionCoreByName - : (decompositionCoreByNameByReformName[reformName] ?? + : (decompositionCoreByNameByReformName[billName] ?? decompositionCoreByName), ) as DecompositionByName, ) @@ -41,7 +40,7 @@ // Note: A reform variable is always more complete than a variable before reform. // But it may contain different formulas, with different parameters & variables. let billVariableSummaryByName = $derived( - variableSummaryByNameByReformName[reformName] ?? variableSummaryByName, + variableSummaryByNameByReformName[billName] ?? variableSummaryByName, ) let waterfallsDecompositions = $derived( diff --git a/src/lib/components/WaterfallView.svelte b/src/lib/components/WaterfallView.svelte index 69d918b7a5b8c342d94578d0313337f798e5e316..df0f985ec0e44f6585061412e801299ced6cf4b6 100644 --- a/src/lib/components/WaterfallView.svelte +++ b/src/lib/components/WaterfallView.svelte @@ -1,25 +1,20 @@ <script lang="ts"> - import type { VariableByName, Waterfall } from "@openfisca/json-model" - import { createEventDispatcher, getContext } from "svelte" - import type { Writable } from "svelte/store" + import type { VariableByName } from "@openfisca/json-model" + import { createEventDispatcher } from "svelte" import { goto } from "$app/navigation" import TestCaseGraph from "$lib/components/test_cases/TestCaseGraph.svelte" import Tooltip from "$lib/components/Tooltip.svelte" import ValueChange from "$lib/components/ValueChange.svelte" - import type { - DecompositionByName, - EvaluationByName, - } from "$lib/decompositions" + import type { EvaluationByName } from "$lib/decompositions" import { buildVisibleDecompositions } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" import { entityByKey, personEntityKey } from "$lib/entities" import { trackTestCaseGraph } from "$lib/matomo" import publicConfig from "$lib/public_config" - import type { ParametricReform } from "$lib/reforms" + import { revaluationName, shared } from "$lib/shared.svelte" import { type ActiveSlider, - type CalculationByName, getCalculatedVariableValueByCalculationName, getSituationVariableValue, setSituationVariableValue, @@ -33,7 +28,6 @@ } from "$lib/variables" interface Props { - decompositionByName: DecompositionByName displayMode: DisplayMode evaluationByName: EvaluationByName highlightDecomposition?: boolean @@ -45,9 +39,9 @@ year: number } - const { householdEntityKey, revaluationName } = publicConfig + const { householdEntityKey } = publicConfig + let { - decompositionByName = $bindable(), displayMode, evaluationByName, highlightDecomposition = false, @@ -59,9 +53,6 @@ year, }: Props = $props() - const calculationByName = getContext( - "calculationByName", - ) as Writable<CalculationByName> const dateFormatter = new Intl.DateTimeFormat("fr-FR", { dateStyle: "full" }) .format const deltaFormatter = (value: number): string => @@ -73,9 +64,6 @@ style: "currency", }).format(removeNegativeZero(value)) const dispatch = createEventDispatcher() - const evaluationByNameArray = getContext("evaluationByNameArray") as Writable< - EvaluationByName[] - > const firstDeltaFormatter = (value: number): string => new Intl.NumberFormat("fr-FR", { currency: "EUR", @@ -84,41 +72,35 @@ style: "currency", }).format(removeNegativeZero(value)) const householdEntity = entityByKey[householdEntityKey] - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> const personEntity = entityByKey[personEntityKey] - const showNulls = getContext("showNulls") as Writable<boolean> - const vectorLength = getContext("vectorLength") as Writable<number> - const waterfall = getContext("waterfall") as Writable<Waterfall> - let useRevaluationInsteadOfLaw = revaluationName !== undefined + let useRevaluationInsteadOfLaw = $derived(revaluationName !== undefined) let firstCalculationName = $derived( useRevaluationInsteadOfLaw ? "revaluation" : "law", ) let runningCalculationNames = $derived( - Object.entries($calculationByName) + Object.entries(shared.calculationByName) .filter(([, calculation]) => calculation.running) .map(([calculationName]) => calculationName), ) let modificationsAmendmentCount = $derived( - Object.keys($parametricReform).length, + Object.keys(shared.parametricReform).length, ) let visibleDecompositions = $derived( buildVisibleDecompositions( - decompositionByName, + shared.decompositionByName, entityByKey, evaluationByName, situation, variableSummaryByName, - $waterfall, - $showNulls, + shared.waterfall, + shared.showNulls, useRevaluationInsteadOfLaw, - $vectorLength, + shared.vectorLength, year, ), ) @@ -282,11 +264,10 @@ } let decomposition = visibleDecomposition.decomposition if (!decomposition.open) { - decompositionByName = { - ...decompositionByName, - [decomposition.name]: { ...decomposition, open: true }, + shared.decompositionByName[decomposition.name] = { + ...decomposition, + open: true, } - dispatch("changeDecompositionByName", decompositionByName) return } } @@ -301,11 +282,10 @@ } let decomposition = visibleDecomposition.decomposition if (decomposition.open) { - decompositionByName = { - ...decompositionByName, - [decomposition.name]: { ...decomposition, open: false }, + shared.decompositionByName[decomposition.name] = { + ...decomposition, + open: false, } - dispatch("changeDecompositionByName", decompositionByName) return } } @@ -425,7 +405,11 @@ class="cursor-pointer overflow-x-hidden text-ellipsis font-serif text-base hover:z-20 hover:overflow-x-visible hover:bg-white hover:text-le-gris-dispositif hover:underline" onclick={() => { // Non-leaf decomposition node in variable inputs mode => no-link - decomposition.open ? zoomOut(index) : zoomIn(index) + if (decomposition.open) { + zoomOut(index) + } else { + zoomIn(index) + } // Leaf decomposition node with parameters in parameters mode => link if (displayMode.edit === undefined) { @@ -600,7 +584,7 @@ type="checkbox" value="" class="peer sr-only" - bind:checked={$showNulls} + bind:checked={shared.showNulls} /> <div class="peer relative h-6 w-11 shrink-0 rounded-full bg-gray-400 after:absolute after:start-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:bg-white after:transition-all after:content-[''] peer-checked:bg-le-vert-500 peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-0" @@ -612,7 +596,7 @@ </div> <!--AJout de la variable niveau de vie--> - {#if $waterfall.name === "brut_to_disponible"} + {#if shared.waterfall.name === "brut_to_disponible"} <div class="relative ml-10 mt-10 flex flex-col border-t-2 border-neutral-500" > @@ -759,18 +743,17 @@ ></iconify-icon> </button> <TestCaseGraph - {decompositionByName} {displayMode} {evaluationByName} - evaluationByNameArray={$evaluationByNameArray} + evaluationByNameArray={shared.evaluationByNameArray} on:changeSituation {situation} {situationIndex} {useRevaluationInsteadOfLaw} {valuesByCalculationNameByVariableName} {variableSummaryByName} - vectorLength={$vectorLength} - waterfall={$waterfall} + vectorLength={shared.vectorLength} + waterfall={shared.waterfall} {year} /> {/if} diff --git a/src/lib/components/budget/BudgetDetailView.svelte b/src/lib/components/budget/BudgetDetailView.svelte index fa2602bcd7ac9bff734c299412fdca67792af2e5..2dcf33e29ebb22dd3c04f590cbc2645c6f58f145 100644 --- a/src/lib/components/budget/BudgetDetailView.svelte +++ b/src/lib/components/budget/BudgetDetailView.svelte @@ -1,8 +1,6 @@ <script lang="ts"> import { scaleBand } from "d3-scale" import { Html, LayerCake, Svg } from "layercake" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import type { BudgetQuantile, @@ -18,6 +16,7 @@ import Tooltip from "$lib/components/Tooltip.svelte" import ValueChange from "$lib/components/ValueChange.svelte" import type { DisplayMode } from "$lib/displays" + import { yearPLF } from "$lib/shared.svelte" import { removeNegativeZero } from "$lib/values" import { type BudgetVariable, @@ -71,7 +70,6 @@ ["one", "er"], ]) let yBase = $state(yBaseZoomed) - const yearPLF = getContext("yearPLF") as Writable<number> let zoomed = $state(true) function buildBudgetChartLegend( @@ -109,7 +107,7 @@ legend.push({ id: "law_base", - label: `${baseVariable.labels.default} ${$yearPLF}`, + label: `${baseVariable.labels.default} ${yearPLF}`, pattern: { backgroundClass: "bg-gray-700", }, @@ -118,7 +116,7 @@ if (hasAllegementCustomization) { legend.push({ id: "law_allegement", - label: `${budgetVariable.labels.default} ${$yearPLF}`, + label: `${budgetVariable.labels.default} ${yearPLF}`, pattern: { backgroundClass: "bg-gray-700", foreground: `data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-opacity='1' fill-rule='evenodd'%3E%3Cpath d='M 40,30 30,40 H 40 Z M 40,10 10,40 H 20 L 40,20 Z M 30,0 0,30 V 40 L 40,0 Z M 10,0 0,10 V 20 L 20,0 Z'/%3E%3C/g%3E%3C/svg%3E`, @@ -850,6 +848,7 @@ ? law.revenus_menage_sum : law.rfr_sum} /> + <!-- TODO svelte5: fix --> <Column fill="#404040" on:click={() => changeQuantileIndex(index)} diff --git a/src/lib/components/budget/BudgetLayout.svelte b/src/lib/components/budget/BudgetLayout.svelte index 7a0030ccf9b016be6910cd6252b6fd3817c5d23e..cf08897ae9a54200e30792980fa19457a1b51816 100644 --- a/src/lib/components/budget/BudgetLayout.svelte +++ b/src/lib/components/budget/BudgetLayout.svelte @@ -1,13 +1,10 @@ <script lang="ts"> - import { getContext } from "svelte" - import type { Writable } from "svelte/store" - import type { BudgetSimulation } from "$lib/budgets" import BudgetDetailView from "$lib/components/budget/BudgetDetailView.svelte" import GagnantsPerdantsView from "$lib/components/budget/GagnantsPerdantsView.svelte" import Tooltip from "$lib/components/Tooltip.svelte" - import type { DecompositionByName } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" + import { billActive, shared } from "$lib/shared.svelte" import { budgetVariableNameByVariableName, budgetVariablesConfig, @@ -23,32 +20,27 @@ let { blur = false, budgetSimulation, displayMode }: Props = $props() - const billActive = getContext("billActive") as Writable<boolean> - const decompositionByName = getContext( - "decompositionByName", - ) as Writable<DecompositionByName> - let budgetVariableName = $derived( displayMode.parametersVariableName !== undefined ? budgetVariableNameByVariableName[displayMode.parametersVariableName] : undefined, ) - let budgetVariable = $derived( - (budgetVariableName + let budgetVariable: + | (BudgetVariable & { label: string; name: string }) + | undefined = $derived( + budgetVariableName ? { ...budgetVariablesConfig[budgetVariableName], label: - $decompositionByName[budgetVariableName]?.short_label ?? + shared.decompositionByName[budgetVariableName]?.short_label ?? variableSummaryByName[budgetVariableName]?.short_label ?? - $decompositionByName[budgetVariableName]?.label ?? + shared.decompositionByName[budgetVariableName]?.label ?? variableSummaryByName[budgetVariableName]?.label ?? budgetVariableName, name: budgetVariableName, } - : undefined) as - | (BudgetVariable & { label: string; name: string }) - | undefined, + : undefined, ) </script> @@ -196,7 +188,7 @@ </div> {/snippet} </Tooltip>. - {#if $billActive} + {#if billActive} <br /> <span class="rounded-sm bg-red-200 px-1 py-0.5 text-black"> ⚠️ En attente des prévisions 2025 ! Montants actuels @@ -249,7 +241,7 @@ </div> {/snippet} </Tooltip>. - {#if $billActive} + {#if billActive} <br /> <span class="rounded-sm bg-red-200 px-1 py-0.5 text-black"> ⚠️ En attente des prévisions 2025 ! Montants actuels @@ -292,7 +284,7 @@ {/snippet} </Tooltip> du rapport des comptes de la Sécurité sociale. - {#if $billActive} + {#if billActive} <br /> <span class="rounded-sm bg-red-200 px-1 py-0.5 text-black"> ⚠️ En attente des prévisions 2025 ! Montants actuels @@ -340,7 +332,7 @@ {/snippet} </Tooltip> du rapport des comptes de la Sécurité sociale. - {#if $billActive} + {#if billActive} <br /> <span class="rounded-sm bg-red-200 px-1 py-0.5 text-black"> ⚠️ En attente des prévisions 2025 ! Montants actuels @@ -349,7 +341,7 @@ {/if} </span> {:else if budgetVariable.name === "vieillesse_salarie" || budgetVariable.name === "vieillesse_deplafonnee_salarie" || budgetVariable.name === "vieillesse_plafonnee_salarie" || budgetVariable.name === "vieillesse_employeur" || budgetVariable.name === "vieillesse_deplafonnee_employeur" || budgetVariable.name === "vieillesse_plafonnee_employeur"} - {#if $billActive} + {#if billActive} Hypothèse de calcul basée sur l'évolution des recettes du PLFSS 2025. <br /> @@ -399,7 +391,7 @@ du rapport des comptes de la Sécurité sociale. {/if} {:else if budgetVariable.name === "mmid_employeur_net_allegement" || budgetVariable.name === "mmid_employeur" || budgetVariable.name === "famille_net_allegement" || budgetVariable.name === "famille"} - {#if $billActive} + {#if billActive} Hypothèse de calcul basée sur l'évolution des recettes du PLFSS 2025. <br /> @@ -465,7 +457,7 @@ du PLFSS pour 2024 et du REPSS 2022. {/if} {:else if budgetVariable.name === "contribution_solidarite_autonomie"} - {#if $billActive} + {#if billActive} Hypothèse de calcul basée sur l'évolution des recettes du PLFSS 2025. <br /> @@ -513,7 +505,7 @@ du PLFSS pour 2024. {/if} {:else if budgetVariable.name === "allegement_cotisation_allocations_familiales" || budgetVariable.name === "allegement_cotisation_maladie" || budgetVariable.name === "allegement_general"} - {#if $billActive} + {#if billActive} Hypothèse de calcul basée sur les recettes d'allègements indiquées dans le PLFSS 2025. <br /> @@ -595,7 +587,7 @@ {/snippet} </Tooltip> du PLF pour 2024. - {#if $billActive} + {#if billActive} <br /> <span class="rounded-sm bg-red-200 px-1 py-0.5 text-black"> ⚠️ En attente des prévisions 2025 ! Montants actuels @@ -636,7 +628,7 @@ {/snippet} </Tooltip> du PLF pour 2024. - {#if $billActive} + {#if billActive} <br /> <span class="rounded-sm bg-red-200 px-1 py-0.5 text-black"> ⚠️ En attente des prévisions 2025 ! Montants actuels diff --git a/src/lib/components/layercake/Column.svelte b/src/lib/components/layercake/Column.svelte index 2fe2f7811a17d7d3a25937d956f4ab8c98bf5cb2..a77558f99a5d163a92e8247d32f663a3aae3e380 100644 --- a/src/lib/components/layercake/Column.svelte +++ b/src/lib/components/layercake/Column.svelte @@ -40,6 +40,8 @@ let yScaled = $derived($yScale(y)) let columnWidth = $derived(xScale.bandwidth()) + + $effect(() => $inspect("!", xScale.range(), xScale.bandwidth())) </script> {#if columnHeight > 0} diff --git a/src/lib/components/parameters/ParameterView.svelte b/src/lib/components/parameters/ParameterView.svelte index 5870e069f87fe14aaabaa2b53a43c3da871742ec..2f25568780503f1d35412dcd20410cee43fbb302 100644 --- a/src/lib/components/parameters/ParameterView.svelte +++ b/src/lib/components/parameters/ParameterView.svelte @@ -8,11 +8,9 @@ type Parameter, } from "@openfisca/json-model" import { getContext } from "svelte" - import type { Writable } from "svelte/store" import { goto } from "$app/navigation" import ScaleView from "$lib/components/parameters/ScaleView.svelte" - import type { DisplayMode } from "$lib/displays" import { metadata } from "$lib/metadata" import { asAmountScaleParameter, @@ -22,6 +20,7 @@ labelFromValueType, } from "$lib/parameters" import publicConfig from "$lib/public_config" + import { shared } from "$lib/shared.svelte" import { getUnitByName, getUnitShortLabel } from "$lib/units" import { type SelfTargetAProps, newSimulationUrl } from "$lib/urls" @@ -31,13 +30,11 @@ } const { openfiscaRepository } = publicConfig + let { date, parameter }: Props = $props() const dateFormatter = new Intl.DateTimeFormat("fr-FR", { dateStyle: "full" }) .format - const displayMode = getContext("displayMode") as Writable< - DisplayMode | undefined - > const newSelfTargetAProps = getContext("newSelfTargetAProps") as ( url: string, ) => SelfTargetAProps @@ -54,7 +51,11 @@ <button class="ml-10 mt-5 inline-flex cursor-pointer items-center rounded bg-gray-200 p-2 pr-3 text-sm text-black shadow-md hover:bg-gray-300 active:bg-gray-400" onclick={() => - goto($displayMode === undefined ? "/" : newSimulationUrl($displayMode))} + goto( + shared.displayMode === undefined + ? "/" + : newSimulationUrl(shared.displayMode), + )} > <iconify-icon class="text-2xl" icon="ri-arrow-left-line"></iconify-icon> <span class="ml-3">Retour au simulateur</span> diff --git a/src/lib/components/test_cases/TestCaseCompareModal.svelte b/src/lib/components/test_cases/TestCaseCompareModal.svelte index 86fd34e02cbf822baf73baee0bd80242a2d438d3..666c1383af8ee6e21caee5e7aeb52a6dc02aeba2 100644 --- a/src/lib/components/test_cases/TestCaseCompareModal.svelte +++ b/src/lib/components/test_cases/TestCaseCompareModal.svelte @@ -1,11 +1,8 @@ <script lang="ts"> import { Dialog } from "bits-ui" - import { createEventDispatcher, getContext } from "svelte" - import type { Writable } from "svelte/store" + import { createEventDispatcher } from "svelte" - import type { EvaluationByName } from "$lib/decompositions" - import type { Situation } from "$lib/situations" - import type { ValuesByCalculationNameByVariableName } from "$lib/variables" + import { shared } from "$lib/shared.svelte" interface Props { isOpen?: boolean @@ -14,66 +11,50 @@ let { isOpen = $bindable(false) }: Props = $props() const dispatch = createEventDispatcher() - const evaluationByNameArray = getContext("evaluationByNameArray") as Writable< - EvaluationByName[] - > - const inputInstantsByVariableNameArray = getContext( - "inputInstantsByVariableNameArray", - ) as Writable< - Array<{ - [name: string]: Set<string> - }> - > - const testCases = getContext("testCases") as Writable<Situation[]> - const testCasesIndex = getContext("testCasesIndex") as Writable<number[]> - let selectedTestCaseIndex = $state($testCasesIndex[1]) - const valuesByCalculationNameByVariableNameArray = getContext( - "valuesByCalculationNameByVariableNameArray", - ) as Writable<ValuesByCalculationNameByVariableName[]> + let selectedTestCaseIndex = $state(shared.testCasesIndex[1]) function closeModal() { isOpen = false - dispatch("changeTestCasesIndex", $testCasesIndex) + dispatch("changeTestCasesIndex", shared.testCasesIndex) } function duplicateTestCaseAndCloseModal() { - $testCasesIndex = [$testCasesIndex[0], $testCases.length] - const duplicatedTestCase = structuredClone($testCases[$testCasesIndex[0]]) + shared.testCasesIndex = [shared.testCasesIndex[0], shared.testCases.length] + const duplicatedTestCase = structuredClone( + shared.testCases[shared.testCasesIndex[0]], + ) duplicatedTestCase.title = `Copie de ${duplicatedTestCase.title}` - $testCases = [...$testCases, duplicatedTestCase] + shared.testCases.push(duplicatedTestCase) const duplicatedEvaluationByName = structuredClone( - $evaluationByNameArray[$testCasesIndex[0]], + shared.evaluationByNameArray[shared.testCasesIndex[0]], ) - $evaluationByNameArray = [ - ...$evaluationByNameArray, - duplicatedEvaluationByName, - ] + shared.evaluationByNameArray.push(duplicatedEvaluationByName) const duplicatedInputInstantsByVariableName = structuredClone( - $inputInstantsByVariableNameArray[$testCasesIndex[0]], + shared.inputInstantsByVariableNameArray[shared.testCasesIndex[0]], ) - $inputInstantsByVariableNameArray = [ - ...$inputInstantsByVariableNameArray, + shared.inputInstantsByVariableNameArray.push( duplicatedInputInstantsByVariableName, - ] + ) const duplicatedValuesByCalculationNameByVariableName = structuredClone( - $valuesByCalculationNameByVariableNameArray[$testCasesIndex[0]], + shared.valuesByCalculationNameByVariableNameArray[ + shared.testCasesIndex[0] + ], ) - $valuesByCalculationNameByVariableNameArray = [ - ...$valuesByCalculationNameByVariableNameArray, + shared.valuesByCalculationNameByVariableNameArray.push( duplicatedValuesByCalculationNameByVariableName, - ] + ) isOpen = false - dispatch("changeTestCasesIndex", $testCasesIndex) + dispatch("changeTestCasesIndex", shared.testCasesIndex) } function selectTestCaseAndCloseModal() { - $testCasesIndex = [$testCasesIndex[0], selectedTestCaseIndex] + shared.testCasesIndex = [shared.testCasesIndex[0], selectedTestCaseIndex] isOpen = false - dispatch("changeTestCasesIndex", $testCasesIndex) + dispatch("changeTestCasesIndex", shared.testCasesIndex) } </script> @@ -115,10 +96,10 @@ disabled value={undefined}>Choisissez un cas type…</option > - {#each $testCases as situation, situationIndex} + {#each shared.testCases as situation, situationIndex} <option class="w-full bg-white py-1 text-sm" - disabled={situationIndex === $testCasesIndex[0]} + disabled={situationIndex === shared.testCasesIndex[0]} value={situationIndex} > {situation.title} @@ -128,7 +109,7 @@ <button class="mb-5 mt-2 inline-flex items-center rounded bg-gray-300 px-2 py-1 text-base uppercase enabled:text-black enabled:shadow-lg enabled:hover:bg-gray-400 disabled:text-gray-500" disabled={selectedTestCaseIndex === undefined || - selectedTestCaseIndex === $testCasesIndex[0]} + selectedTestCaseIndex === shared.testCasesIndex[0]} type="button" onclick={selectTestCaseAndCloseModal} >Comparer <iconify-icon diff --git a/src/lib/components/test_cases/TestCaseCompareView.svelte b/src/lib/components/test_cases/TestCaseCompareView.svelte index 14087cb3084f4e0043bae6c00805b45274f94531..4027424adb50a83c9d38faa58382bef221e34be5 100644 --- a/src/lib/components/test_cases/TestCaseCompareView.svelte +++ b/src/lib/components/test_cases/TestCaseCompareView.svelte @@ -1,58 +1,30 @@ <script lang="ts"> - import type { DecompositionReference, Waterfall } from "@openfisca/json-model" - import { createEventDispatcher, getContext } from "svelte" - import type { Writable } from "svelte/store" + import type { DecompositionReference } from "@openfisca/json-model" + import { createEventDispatcher } from "svelte" import type { CalculationName } from "$lib/calculations.svelte" import TestCaseSummary from "$lib/components/test_cases/TestCaseSummary.svelte" import ValueChangeCompare from "$lib/components/ValueChangeCompare.svelte" import WaterfallCompareView from "$lib/components/WaterfallCompareView.svelte" - import { - type DecompositionByName, - type EvaluationByName, - waterfalls, - } from "$lib/decompositions" + import { type EvaluationByName, waterfalls } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" - import publicConfig from "$lib/public_config" - import type { ParametricReform } from "$lib/reforms" - import type { Situation } from "$lib/situations" + import { billName, revaluationName, shared } from "$lib/shared.svelte" import { newSimulationUrl } from "$lib/urls" - import { - type ValuesByCalculationNameByVariableName, - variableSummaryByName, - } from "$lib/variables" + import { variableSummaryByName } from "$lib/variables" interface Props { - decompositionByName: DecompositionByName displayMode: DisplayMode situationsToCompareIndex: number[] year: number } - const { reformName, revaluationName } = publicConfig - let { - decompositionByName, - displayMode, - situationsToCompareIndex, - year, - }: Props = $props() + let { displayMode, situationsToCompareIndex, year }: Props = $props() - const evaluationByNameArray = getContext("evaluationByNameArray") as Writable< - EvaluationByName[] - > - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const testCases = getContext("testCases") as Writable<Situation[]> - const testCasesIndex = getContext("testCasesIndex") as Writable<number[]> - const valuesByCalculationNameByVariableNameArray = getContext( - "valuesByCalculationNameByVariableNameArray", - ) as Writable<ValuesByCalculationNameByVariableName[]> - const waterfall = getContext("waterfall") as Writable<Waterfall> + const dispatch = createEventDispatcher() let situationsToCompare = $derived( situationsToCompareIndex.map( - (situationIndex) => $testCases[situationIndex], + (situationIndex) => shared.testCases[situationIndex], ), ) @@ -93,7 +65,7 @@ <button class="text-sm uppercase text-gray-600 hover:text-black" onclick={() => - dispatch("changeTestCasesIndex", $testCasesIndex.slice(0, 1))} + dispatch("changeTestCasesIndex", shared.testCasesIndex.slice(0, 1))} type="button" title="Fermer la comparaison des deux cas types" > @@ -122,9 +94,8 @@ mode="compare" {situation} {situationIndex} - valuesByCalculationNameByVariableName={$valuesByCalculationNameByVariableNameArray[ - situationIndex - ]} + valuesByCalculationNameByVariableName={shared + .valuesByCalculationNameByVariableNameArray[situationIndex]} {year} /> </div> @@ -146,7 +117,7 @@ > <div class="mb-4 w-full flex-col text-gray-700"> <p class="mb-1 text-base font-medium"> - {$waterfall.totalLabel}<svg + {shared.waterfall.totalLabel}<svg aria-hidden="true" class="mx-1 inline-flex h-4 w-4 fill-current text-white" viewBox="0 0 24 22" @@ -169,72 +140,84 @@ unitName="currency-EUR" valueByCalculationName0={{ amendment: - Object.keys($parametricReform).length === 0 + Object.keys(shared.parametricReform).length === 0 ? undefined : calculateTotal( - $evaluationByNameArray[situationsToCompareIndex[0]], + shared.evaluationByNameArray[ + situationsToCompareIndex[0] + ], "amendment", - $waterfall.root, - $waterfall.total, + shared.waterfall.root, + shared.waterfall.total, ), bill: - reformName === undefined + billName === undefined ? undefined : calculateTotal( - $evaluationByNameArray[situationsToCompareIndex[0]], + shared.evaluationByNameArray[ + situationsToCompareIndex[0] + ], "bill", - $waterfall.root, - $waterfall.total, + shared.waterfall.root, + shared.waterfall.total, ), law: calculateTotal( - $evaluationByNameArray[situationsToCompareIndex[0]], + shared.evaluationByNameArray[situationsToCompareIndex[0]], "law", - $waterfall.root, - $waterfall.total, + shared.waterfall.root, + shared.waterfall.total, ), revaluation: revaluationName === undefined ? undefined : calculateTotal( - $evaluationByNameArray[situationsToCompareIndex[0]], + shared.evaluationByNameArray[ + situationsToCompareIndex[0] + ], "revaluation", - $waterfall.root, - $waterfall.total, + shared.waterfall.root, + shared.waterfall.total, ), }} valueByCalculationName1={{ amendment: - Object.keys($parametricReform).length === 0 + Object.keys(shared.parametricReform).length === 0 ? undefined : calculateTotal( - $evaluationByNameArray[situationsToCompareIndex[1]], + shared.evaluationByNameArray[ + situationsToCompareIndex[1] + ], "amendment", - $waterfall.root, - $waterfall.total, + shared.waterfall.root, + shared.waterfall.total, ), bill: - reformName === undefined + billName === undefined ? undefined : calculateTotal( - $evaluationByNameArray[situationsToCompareIndex[1]], + shared.evaluationByNameArray[ + situationsToCompareIndex[1] + ], "bill", - $waterfall.root, - $waterfall.total, + shared.waterfall.root, + shared.waterfall.total, ), law: calculateTotal( - $evaluationByNameArray[situationsToCompareIndex[1]], + shared.evaluationByNameArray[situationsToCompareIndex[1]], "law", - $waterfall.root, - $waterfall.total, + shared.waterfall.root, + shared.waterfall.total, ), revaluation: revaluationName === undefined ? undefined : calculateTotal( - $evaluationByNameArray[situationsToCompareIndex[1]], + shared.evaluationByNameArray[ + situationsToCompareIndex[1] + ], "revaluation", - $waterfall.root, - $waterfall.total, + shared.waterfall.root, + shared.waterfall.total, ), }} /> @@ -250,13 +233,15 @@ </p> </div> --> </div> - {#if displayMode.parametersVariableName !== undefined && decompositionByName[displayMode.parametersVariableName] !== undefined} + {#if displayMode.parametersVariableName !== undefined && shared.decompositionByName[displayMode.parametersVariableName] !== undefined} <div class="mt-2 flex-col"> <p class="mb-1 text-lg font-black"> - {decompositionByName[displayMode.parametersVariableName] - .short_label ?? - decompositionByName[displayMode.parametersVariableName] - .label ?? + {shared.decompositionByName[ + displayMode.parametersVariableName + ].short_label ?? + shared.decompositionByName[ + displayMode.parametersVariableName + ].label ?? displayMode.parametersVariableName} : </p> <div class="text-2xl font-semibold"> @@ -264,30 +249,31 @@ unitName="currency-EUR" valueByCalculationName0={{ amendment: - Object.keys($parametricReform).length === 0 + Object.keys(shared.parametricReform).length === 0 ? undefined - : ($evaluationByNameArray[ + : (shared.evaluationByNameArray[ situationsToCompareIndex[0] ][displayMode.parametersVariableName] ?.calculationEvaluationByName["amendment"] ?.deltaAtVectorIndex ?? 0), bill: - reformName === undefined + billName === undefined ? undefined - : ($evaluationByNameArray[ + : (shared.evaluationByNameArray[ situationsToCompareIndex[0] ][displayMode.parametersVariableName] ?.calculationEvaluationByName["bill"] ?.deltaAtVectorIndex ?? 0), law: - $evaluationByNameArray[situationsToCompareIndex[0]][ - displayMode.parametersVariableName - ]?.calculationEvaluationByName["law"] + shared.evaluationByNameArray[ + situationsToCompareIndex[0] + ][displayMode.parametersVariableName] + ?.calculationEvaluationByName["law"] ?.deltaAtVectorIndex ?? 0, revaluation: revaluationName === undefined ? undefined - : ($evaluationByNameArray[ + : (shared.evaluationByNameArray[ situationsToCompareIndex[0] ][displayMode.parametersVariableName] ?.calculationEvaluationByName["revaluation"] @@ -295,30 +281,31 @@ }} valueByCalculationName1={{ amendment: - Object.keys($parametricReform).length === 0 + Object.keys(shared.parametricReform).length === 0 ? undefined - : ($evaluationByNameArray[ + : (shared.evaluationByNameArray[ situationsToCompareIndex[1] ][displayMode.parametersVariableName] ?.calculationEvaluationByName["amendment"] ?.deltaAtVectorIndex ?? 0), bill: - reformName === undefined + billName === undefined ? undefined - : ($evaluationByNameArray[ + : (shared.evaluationByNameArray[ situationsToCompareIndex[1] ][displayMode.parametersVariableName] ?.calculationEvaluationByName["bill"] ?.deltaAtVectorIndex ?? 0), law: - $evaluationByNameArray[situationsToCompareIndex[1]][ - displayMode.parametersVariableName - ]?.calculationEvaluationByName["law"] + shared.evaluationByNameArray[ + situationsToCompareIndex[1] + ][displayMode.parametersVariableName] + ?.calculationEvaluationByName["law"] ?.deltaAtVectorIndex ?? 0, revaluation: revaluationName === undefined ? undefined - : ($evaluationByNameArray[ + : (shared.evaluationByNameArray[ situationsToCompareIndex[1] ][displayMode.parametersVariableName] ?.calculationEvaluationByName["revaluation"] @@ -330,11 +317,9 @@ {/if} </div> <WaterfallCompareView - {decompositionByName} {displayMode} - evaluationByNameArray={$evaluationByNameArray} - on:changeDecompositionByName - situations={$testCases} + evaluationByNameArray={shared.evaluationByNameArray} + situations={shared.testCases} {situationsToCompareIndex} {variableSummaryByName} {year} @@ -347,10 +332,10 @@ {#each waterfalls as { icon, label, name }} <a class="flex grow items-center justify-center shadow-inner" - class:bg-white={name === $waterfall.name} - class:border-le-bleu={name === $waterfall.name} - class:border-r-4={name === $waterfall.name} - class:shadow-none={name === $waterfall.name} + class:bg-white={name === shared.waterfall.name} + class:border-le-bleu={name === shared.waterfall.name} + class:border-r-4={name === shared.waterfall.name} + class:shadow-none={name === shared.waterfall.name} href={newSimulationUrl({ ...displayMode, waterfallName: name, @@ -368,8 +353,8 @@ {/if} <span class="ml-2 text-xs uppercase tracking-wide text-gray-500 xl:text-sm" - class:text-le-bleu={name === $waterfall.name} - class:font-bold={name === $waterfall.name} + class:text-le-bleu={name === shared.waterfall.name} + class:font-bold={name === shared.waterfall.name} > {label} </span> diff --git a/src/lib/components/test_cases/TestCaseEditVariablesSearch.svelte b/src/lib/components/test_cases/TestCaseEditVariablesSearch.svelte index 7232cf65d39b7d960730312f14119859fb75dd77..a58a002a91e749375fa141ad2bfafa841e24464e 100644 --- a/src/lib/components/test_cases/TestCaseEditVariablesSearch.svelte +++ b/src/lib/components/test_cases/TestCaseEditVariablesSearch.svelte @@ -1,11 +1,9 @@ <script lang="ts"> import type { Variable } from "@openfisca/json-model" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import VariableInput from "$lib/components/variables/VariableInput.svelte" - import publicConfig from "$lib/public_config" import { parseSearch } from "$lib/search/regexp_search" + import { billName } from "$lib/shared.svelte" import type { Situation } from "$lib/situations" import { type ValuesByCalculationNameByVariableName, @@ -24,7 +22,6 @@ year: number } - const { reformName } = publicConfig let { date, inputInstantsByVariableName = $bindable(), @@ -52,7 +49,7 @@ // Note: A reform variable is always more complete than a variable before reform. // But it may contain different formulas, with different parameters & variables. let billVariableSummaryByName = $derived( - variableSummaryByNameByReformName[reformName] ?? variableSummaryByName, + variableSummaryByNameByReformName[billName] ?? variableSummaryByName, ) let inputVariables = $derived( (Object.values(billVariableSummaryByName) as Variable[]).filter( diff --git a/src/lib/components/test_cases/TestCaseFilters.svelte b/src/lib/components/test_cases/TestCaseFilters.svelte index d813a19348eeec0403acb65fcfa24d8f22be6c0f..2f9a5194c004155972ea97264c576dfccefcebef 100644 --- a/src/lib/components/test_cases/TestCaseFilters.svelte +++ b/src/lib/components/test_cases/TestCaseFilters.svelte @@ -1,12 +1,9 @@ <script lang="ts"> - import { run } from "svelte/legacy" - import { scaleByInstantFromBrackets, type Variable, } from "@openfisca/json-model" - import { createEventDispatcher, getContext } from "svelte" - import type { Writable } from "svelte/store" + import { createEventDispatcher } from "svelte" import { page } from "$app/stores" import SelectChip from "$lib/components/SelectChip.svelte" @@ -16,7 +13,6 @@ import { decompositionCoreByName, decompositionCoreByNameByReformName, - type EvaluationByName, getLatestCalculation, } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" @@ -28,10 +24,10 @@ rootParameterByReformName, } from "$lib/parameters" import publicConfig from "$lib/public_config" + import { billName, shared } from "$lib/shared.svelte" import { getSituationVariableValue, type Situation } from "$lib/situations" import { valueFormatter } from "$lib/values" import { - type ValuesByCalculationNameByVariableName, variableSummaryByName, variableSummaryByNameByReformName, type VariableValue, @@ -45,8 +41,8 @@ year: number } - const { childrenKey, familyEntityKey, householdEntityKey, reformName } = - publicConfig + const { childrenKey, familyEntityKey, householdEntityKey } = publicConfig + let { displayMode, showOnlyDeciles = $bindable(false), @@ -57,9 +53,6 @@ let descriptionsOpen = $state(false) const dispatch = createEventDispatcher() - const evaluationByNameArray = getContext("evaluationByNameArray") as Writable< - EvaluationByName[] - > const personEntity = entityByKey[personEntityKey] const familyEntity = entityByKey[familyEntityKey] const householdEntity = entityByKey[householdEntityKey] @@ -74,9 +67,6 @@ ["one", "er"], ]) const ordinalPluralRules = new Intl.PluralRules("fr-FR", { type: "ordinal" }) - const valuesByCalculationNameByVariableNameArray = getContext( - "valuesByCalculationNameByVariableNameArray", - ) as Writable<ValuesByCalculationNameByVariableName[]> // Filters let filters: { @@ -139,6 +129,110 @@ let filterNiveauDeVieValue: string | undefined = $state(undefined) + // Note: A reform decomposition is always more complete than a decomposition before reform. + // And the children of a reform decomposition always contain the children of the decomposition + // before reform. + // → Non reform decomposition is not needed. + let latestDecompositionCoreByName = $derived( + decompositionCoreByNameByReformName[billName] ?? decompositionCoreByName, + ) + let latestDecompositionCore = $derived( + latestDecompositionCoreByName[variableName], + ) + let decomposition = $derived( + latestDecompositionCore === undefined + ? undefined + : { + ...latestDecompositionCore, + variableName, + }, + ) + // Note: A reform variable is always more complete than a variable before reform. + // But it may contain different formulas, with different parameters & variables. + let latestVariableSummaryByName = $derived( + variableSummaryByNameByReformName[billName] ?? variableSummaryByName, + ) + let variable = $derived(latestVariableSummaryByName[variableName]) + $effect(() => { + if ( + variableName !== undefined && + decomposition === undefined && + variable === undefined + ) { + console.error(`Variable "${variableName}" not found`) + } + }) + // Note: A reform parameters tree is always more complete than a parameters tree before reform. + // And the children of a reform node parameter always contain the children of the node parameter + // before reform (albeit with some different value parameters). + let billRootParameter = $derived( + rootParameterByReformName[billName] ?? rootParameter, + ) + let decilesNiveauDeVieParameter = $derived( + asScaleParameter(getParameter(billRootParameter, "deciles_niveau_de_vie")), + ) + let decilesNiveauDeVieScaleByInstant = $derived( + scaleByInstantFromBrackets(decilesNiveauDeVieParameter.brackets), + ) + let decilesNiveauDeVieInstantScaleCouplesArray = $derived( + Object.entries(decilesNiveauDeVieScaleByInstant).sort( + ([instant1], [instant2]) => instant2.localeCompare(instant1), + ), + ) + let decilesNiveauDeVieLatestInstantScaleCouple = $derived( + decilesNiveauDeVieInstantScaleCouplesArray[0], + ) + let decilesNiveauDeVieScaleAtInstant = $derived( + decilesNiveauDeVieLatestInstantScaleCouple[1], + ) + let filterValueByName = $derived( + Object.fromEntries(filters.map((filter) => [filter.name, filter.value])), + ) + let variableSummary = $derived( + variableName !== undefined + ? billName === undefined + ? variableSummaryByName[variableName] + : variableSummaryByNameByReformName[billName][variableName] + : undefined, + ) + let filteredTestCases = $derived( + filterTestCases( + filterValueByName, + showOnlyDeciles, + filterNiveauDeVieValue, + variableSummary, + ), + ) + let selectDecilesNiveauDeVie = $derived([ + { + label: "Tous", + shortLabel: "tous", + value: "tous", + }, + { + label: "1<sup>er</sup> décile - à partir de 0 €", + shortLabel: "1<sup>er</sup> décile", + value: "1", + }, + ...(decilesNiveauDeVieScaleAtInstant ?? []).map(({ rate, threshold }) => ({ + label: `${formatLongOrdinalSup(Number(rate.value) + 1)} décile - à partir de ${formatCurrency(threshold.value)}`, + shortLabel: `${formatLongOrdinalSup(Number(rate.value) + 1)} décile`, + value: `${rate.value + 1}`, + })), + ] as { label: string; shortLabel: string; value: string }[]) + let shortLabel = $derived( + decomposition?.short_label ?? + variable?.short_label ?? + decomposition?.name ?? + variable?.name, + ) + + $effect(() => { + if (showOnlyDeciles) { + filterNiveauDeVieValue = undefined + } + }) + function filterTestCases( filterValueByName: { [filter: string]: boolean | string @@ -320,7 +414,7 @@ if (filterValueByName["dispositif"] && variableSummary !== undefined) { let show = false const latestCalculationValue = getLatestCalculation( - $evaluationByNameArray[index][variableName] + shared.evaluationByNameArray[index][variableName] ?.calculationEvaluationByName, )?.deltaAtVectorIndex if ( @@ -348,8 +442,6 @@ continue } } - } else { - filterNiveauDeVieValue = undefined } filtered.push([situation, index]) } @@ -367,103 +459,6 @@ } return getSituationVariableValue(situation, variable, populationId, year) } - // Note: A reform decomposition is always more complete than a decomposition before reform. - // And the children of a reform decomposition always contain the children of the decomposition - // before reform. - // → Non reform decomposition is not needed. - let latestDecompositionCoreByName = $derived( - decompositionCoreByNameByReformName[reformName] ?? decompositionCoreByName, - ) - let latestDecompositionCore = $derived( - latestDecompositionCoreByName[variableName], - ) - let decomposition = $derived( - latestDecompositionCore === undefined - ? undefined - : { - ...latestDecompositionCore, - variableName, - }, - ) - // Note: A reform variable is always more complete than a variable before reform. - // But it may contain different formulas, with different parameters & variables. - let latestVariableSummaryByName = $derived( - variableSummaryByNameByReformName[reformName] ?? variableSummaryByName, - ) - let variable = $derived(latestVariableSummaryByName[variableName]) - run(() => { - if ( - variableName !== undefined && - decomposition === undefined && - variable === undefined - ) { - console.error(`Variable "${variableName}" not found`) - } - }) - // Note: A reform parameters tree is always more complete than a parameters tree before reform. - // And the children of a reform node parameter always contain the children of the node parameter - // before reform (albeit with some different value parameters). - let billRootParameter = $derived( - rootParameterByReformName[reformName] ?? rootParameter, - ) - let decilesNiveauDeVieParameter = $derived( - asScaleParameter(getParameter(billRootParameter, "deciles_niveau_de_vie")), - ) - let decilesNiveauDeVieScaleByInstant = $derived( - scaleByInstantFromBrackets(decilesNiveauDeVieParameter.brackets), - ) - let decilesNiveauDeVieInstantScaleCouplesArray = $derived( - Object.entries(decilesNiveauDeVieScaleByInstant).sort( - ([instant1], [instant2]) => instant2.localeCompare(instant1), - ), - ) - let decilesNiveauDeVieLatestInstantScaleCouple = $derived( - decilesNiveauDeVieInstantScaleCouplesArray[0], - ) - let decilesNiveauDeVieScaleAtInstant = $derived( - decilesNiveauDeVieLatestInstantScaleCouple[1], - ) - let filterValueByName = $derived( - Object.fromEntries(filters.map((filter) => [filter.name, filter.value])), - ) - let variableSummary = $derived( - variableName !== undefined - ? reformName === undefined - ? variableSummaryByName[variableName] - : variableSummaryByNameByReformName[reformName][variableName] - : undefined, - ) - let filteredTestCases = $derived( - filterTestCases( - filterValueByName, - showOnlyDeciles, - filterNiveauDeVieValue, - variableSummary, - ), - ) - let selectDecilesNiveauDeVie = $derived([ - { - label: "Tous", - shortLabel: "tous", - value: "tous", - }, - { - label: "1<sup>er</sup> décile - à partir de 0 €", - shortLabel: "1<sup>er</sup> décile", - value: "1", - }, - ...(decilesNiveauDeVieScaleAtInstant ?? []).map(({ rate, threshold }) => ({ - label: `${formatLongOrdinalSup(Number(rate.value) + 1)} décile - à partir de ${formatCurrency(threshold.value)}`, - shortLabel: `${formatLongOrdinalSup(Number(rate.value) + 1)} décile`, - value: `${rate.value + 1}`, - })), - ] as { label: string; shortLabel: string; value: string }[]) - let shortLabel = $derived( - decomposition?.short_label ?? - variable?.short_label ?? - decomposition?.name ?? - variable?.name, - ) </script> <span class="block text-gray-600"> Filtrer par : </span> @@ -618,9 +613,8 @@ mode="select" situation={testCase} situationIndex={index} - valuesByCalculationNameByVariableName={$valuesByCalculationNameByVariableNameArray[ - index - ]} + valuesByCalculationNameByVariableName={shared + .valuesByCalculationNameByVariableNameArray[index]} {year} /> </div> @@ -636,7 +630,7 @@ {shortLabel} : <VariableValueChange bold={true} - evaluationByName={$evaluationByNameArray[index]} + evaluationByName={shared.evaluationByNameArray[index]} inline name={variableName} /> diff --git a/src/lib/components/test_cases/TestCaseGraph.svelte b/src/lib/components/test_cases/TestCaseGraph.svelte index 5ded260dff3b03f9b028c64548b6bd2ae4247c01..d82aea47f6af8978b014cc9207a5893acef17632 100644 --- a/src/lib/components/test_cases/TestCaseGraph.svelte +++ b/src/lib/components/test_cases/TestCaseGraph.svelte @@ -11,8 +11,7 @@ type RateBracketAtInstant, scaleByInstantFromBrackets, } from "@openfisca/json-model" - import { createEventDispatcher, getContext } from "svelte" - import type { Writable } from "svelte/store" + import { createEventDispatcher } from "svelte" import { fade } from "svelte/transition" import { page } from "$app/stores" @@ -39,7 +38,6 @@ import ValueChangeGraph from "$lib/components/ValueChangeGraph.svelte" import { buildVisibleDecompositionsForGraph, - type DecompositionByName, type EvaluationByName, type VisibleDecompositionForGraph, } from "$lib/decompositions" @@ -59,9 +57,9 @@ rootParameterByReformName, } from "$lib/parameters" import publicConfig from "$lib/public_config" + import { billActive, billName, shared, yearPLF } from "$lib/shared.svelte" import { type ActiveSlider, - type CalculationByName, getSituationVariableValue, setSituationVariableValue, type Situation, @@ -73,7 +71,6 @@ } from "$lib/variables" interface Props { - decompositionByName: DecompositionByName displayMode: DisplayMode evaluationByName: EvaluationByName evaluationByNameArray: EvaluationByName[] | undefined @@ -87,9 +84,9 @@ year: number } - const { childrenKey, familyEntityKey, reformName } = publicConfig + const { childrenKey, familyEntityKey } = publicConfig + let { - decompositionByName, displayMode, evaluationByName, evaluationByNameArray, @@ -103,10 +100,6 @@ year, }: Props = $props() - const billActive = getContext("billActive") as Writable<boolean> - const calculationByName = getContext( - "calculationByName", - ) as Writable<CalculationByName> const dispatch = createEventDispatcher() const familyEntity = entityByKey[familyEntityKey] const formatCurrency = valueFormatter(0, "currency-EUR", false) @@ -131,7 +124,6 @@ ["one", "er"], ]) let svgPadding = { bottom: 20, left: 8, right: 20, top: 20 } - const yearPLF = getContext("yearPLF") as Writable<number> let grapheExplanationOpen = $state(false) let maxVariableValue: number = $state() @@ -528,7 +520,7 @@ let visibleDecompositionsGraph = $derived( situation.slider !== undefined ? buildVisibleDecompositionsForGraph( - decompositionByName, + shared.decompositionByName, entityByKey, evaluationByName, situation, @@ -811,7 +803,7 @@ // And the children of a reform node parameter always contain the children of the node parameter // before reform (albeit with some different value parameters). let billRootParameter = $derived( - rootParameterByReformName[reformName] ?? rootParameter, + rootParameterByReformName[billName] ?? rootParameter, ) let decilesNiveauDeVieParameter = $derived( asScaleParameter(getParameter(billRootParameter, "deciles_niveau_de_vie")), @@ -863,7 +855,7 @@ ), ) let isCalculationRunning = $derived( - Object.values($calculationByName).filter( + Object.values(shared.calculationByName).filter( (calculation) => calculation.running && (calculation.situationIndex === undefined || @@ -1072,8 +1064,9 @@ </div> {/if} <span class="mx-2"> - {decompositionByName[variable.name]?.short_label ?? - decompositionByName[variable.name]?.label} + {shared.decompositionByName[variable.name] + ?.short_label ?? + shared.decompositionByName[variable.name]?.label} {row.calculationName === "amendment" ? "(réforme)" : row.calculationName === "bill" @@ -1641,10 +1634,10 @@ <span class="font-normal text-black"> Cette échelle indique à quel décile de niveau de vie correspondent les salaires des 100 cas types fictifs - du graphique, dans le cadre du {#if $billActive} - droit {$yearPLF} sans PLF/PLSS + du graphique, dans le cadre du {#if billActive} + droit {yearPLF} sans PLF/PLSS {:else} - droit {$yearPLF - 1} + droit {yearPLF - 1} {/if}. Elle s'appuie sur les <a class="lx-link-text" @@ -1670,10 +1663,10 @@ {/snippet} </Tooltip> <span class="text-nowrap text-xs" - >({#if $billActive} - droit {$yearPLF} <br />sans PLF/PLSS + >({#if billActive} + droit {yearPLF} <br />sans PLF/PLSS {:else} - droit {$yearPLF - 1} + droit {yearPLF - 1} {/if})</span > {:else} diff --git a/src/lib/components/test_cases/TestCaseGraphXlsxExport.svelte b/src/lib/components/test_cases/TestCaseGraphXlsxExport.svelte index 07a0e70daadc1e1b2a868f25da46778f1e6679f4..b9bc42bb3c103300b5a070296c5522452e8245ea 100644 --- a/src/lib/components/test_cases/TestCaseGraphXlsxExport.svelte +++ b/src/lib/components/test_cases/TestCaseGraphXlsxExport.svelte @@ -9,8 +9,6 @@ type VariableByName, type Waterfall, } from "@openfisca/json-model" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import XLSX from "xlsx-js-style" import { page } from "$app/stores" @@ -31,10 +29,8 @@ rootParameterByReformName, } from "$lib/parameters" import publicConfig from "$lib/public_config" - import { - ParameterReformChangeType, - type ParametricReform, - } from "$lib/reforms" + import { ParameterReformChangeType } from "$lib/reforms" + import { billActive, billName, budgetDate, shared } from "$lib/shared.svelte" import { publishTestCaseSimulation } from "$lib/simulations" import { getSituationVariableValue, @@ -59,7 +55,8 @@ year: number } - const { baseUrl, reformName } = publicConfig + const { baseUrl } = publicConfig + let { displayMode, domain, @@ -71,8 +68,6 @@ year, }: Props = $props() - const billActive = getContext("billActive") as Writable<boolean> - const budgetDate = getContext("budgetDate") as Writable<string> const dateFormatter = new Intl.DateTimeFormat("fr-FR", { day: "2-digit", month: "2-digit", @@ -90,23 +85,12 @@ : unit?.ratio ? parseFloat((value * 100).toFixed(8)) // trick to round value * 100 : value - const inputInstantsByVariableNameArray = getContext( - "inputInstantsByVariableNameArray", - ) as Writable< - Array<{ - [name: string]: Set<string> - }> - > - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const testCases = getContext("testCases") as Writable<Situation[]> // Note: A reform parameters tree is always more complete than a parameters tree before reform. // And the children of a reform node parameter always contain the children of the node parameter // before reform (albeit with some different value parameters). let billRootParameter = $derived( - rootParameterByReformName[reformName] ?? rootParameter, + rootParameterByReformName[billName] ?? rootParameter, ) async function xlsxExport() { @@ -131,9 +115,9 @@ const token = await publishTestCaseSimulation( null, // blobImageOpenGraph displayMode, - $inputInstantsByVariableNameArray, - $parametricReform, - $testCases, + shared.inputInstantsByVariableNameArray, + shared.parametricReform, + shared.testCases, ) const sheetData = [ [], @@ -225,7 +209,7 @@ const calculationRows = [] // "Impact code en vigueur" sheet: { - const sheetName = $billActive + const sheetName = billActive ? `Impact Code ${year} sans PLF.SS` : "Impact code en vigueur" const sheetData = [] @@ -252,7 +236,7 @@ } // "Impact PLF.SS" sheet: - if ($billActive) { + if (billActive) { const sheetName = `Impact PLF.SS ${year}` const sheetData = [] // Add the column titles @@ -280,11 +264,11 @@ // "Impact Réforme" sheet: { - const sheetName = $billActive + const sheetName = billActive ? `Impact Réforme ${year} vs PLF.SS` : "Impact Réforme" const sheetData = [] - if (Object.keys($parametricReform).length > 0) { + if (Object.keys(shared.parametricReform).length > 0) { // Add the column titles sheetData.push([ "Cas type", @@ -323,7 +307,7 @@ [], ] for (const [parameterName, parameterReform] of Object.entries( - $parametricReform, + shared.parametricReform, )) { const parameter = getParameter(billRootParameter, parameterName) if (parameterReform.type === ParameterReformChangeType.Parameter) { @@ -336,7 +320,7 @@ for (const [i, bracketAtInstant] of parameterReform.scale.entries()) { const thresholdUnit = getUnitAtDate( (parameter as ScaleParameter).threshold_unit, - $budgetDate, + budgetDate, ) const thresholdValue = @@ -357,7 +341,7 @@ if (isAmountScale) { const amountUnit = getUnitAtDate( asAmountScaleParameter(parameter as ScaleParameter).amount_unit, - $budgetDate, + budgetDate, ) const amountValue = @@ -388,7 +372,7 @@ const rateUnit = getUnitAtDate( asRateScaleParameter(parameter as ScaleParameter).rate_unit, - $budgetDate, + budgetDate, ) const rateValue = @@ -440,7 +424,7 @@ variablesName = new Set([...variablesName, ...newVariablesName]) } const inputInstantsByVariableName = - $inputInstantsByVariableNameArray[situationIndex] + shared.inputInstantsByVariableNameArray[situationIndex] const entityLabelByKey: { [key: string]: string } = { famille: "Famille", foyer_fiscal: "Foyer fiscal", diff --git a/src/lib/components/test_cases/TestCaseScreenshotLayout.svelte b/src/lib/components/test_cases/TestCaseScreenshotLayout.svelte index 2ae9a40f18408dea8f574c4ee6a4a57abe41da9a..51dad7546e6db2f5be15cfcaafc74cfc9e26ed31 100644 --- a/src/lib/components/test_cases/TestCaseScreenshotLayout.svelte +++ b/src/lib/components/test_cases/TestCaseScreenshotLayout.svelte @@ -1,11 +1,9 @@ <script lang="ts"> import TestCaseSummary from "$lib/components/test_cases/TestCaseSummary.svelte" import VariableValueChange from "$lib/components/variables/VariableValueChange.svelte" - import type { - DecompositionByName, - EvaluationByName, - } from "$lib/decompositions" + import type { EvaluationByName } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" + import { shared } from "$lib/shared.svelte" import type { Situation } from "$lib/situations" import { type ValuesByCalculationNameByVariableName, @@ -13,7 +11,6 @@ } from "$lib/variables.js" interface Props { - decompositionByName: DecompositionByName displayMode: DisplayMode evaluationByNameArray: EvaluationByName[] testCase: Situation @@ -23,7 +20,6 @@ } let { - decompositionByName, displayMode, evaluationByNameArray, testCase, @@ -71,11 +67,12 @@ > {#if displayMode.parametersVariableName !== undefined} <p class="block px-1 text-start text-sm text-gray-500"> - {decompositionByName[displayMode.parametersVariableName] + {shared.decompositionByName[displayMode.parametersVariableName] ?.short_label ?? variableSummaryByName[displayMode.parametersVariableName] ?.short_label ?? - decompositionByName[displayMode.parametersVariableName]?.label ?? + shared.decompositionByName[displayMode.parametersVariableName] + ?.label ?? variableSummaryByName[displayMode.parametersVariableName] ?.label} : <VariableValueChange diff --git a/src/lib/components/test_cases/TestCaseSelectModal.svelte b/src/lib/components/test_cases/TestCaseSelectModal.svelte index fab01e169c1db221fd530f995cd721974f8fe2a7..82a4fb82adc7713055876a65dc746a9e549a4f38 100644 --- a/src/lib/components/test_cases/TestCaseSelectModal.svelte +++ b/src/lib/components/test_cases/TestCaseSelectModal.svelte @@ -1,12 +1,12 @@ <script lang="ts"> import { Dialog } from "bits-ui" - import { createEventDispatcher, getContext } from "svelte" - import type { Writable } from "svelte/store" + import { createEventDispatcher } from "svelte" + import { fade } from "svelte/transition" import { goto } from "$app/navigation" import TestCaseFilters from "$lib/components/test_cases/TestCaseFilters.svelte" import type { DisplayMode } from "$lib/displays" - import type { Situation } from "$lib/situations" + import { shared } from "$lib/shared.svelte" import { newSimulationUrl } from "$lib/urls" interface Props { @@ -18,8 +18,6 @@ let { displayMode, isOpen = $bindable(false), year }: Props = $props() const dispatch = createEventDispatcher() - const testCases = getContext("testCases") as Writable<Situation[]> - const testCasesIndex = getContext("testCasesIndex") as Writable<number[]> function changeTestCaseIndex(value: number) { isOpen = false @@ -37,7 +35,7 @@ onOpenChange={(open) => { isOpen = open if (!isOpen) { - dispatch("changeTestCasesIndex", $testCasesIndex) + dispatch("changeTestCasesIndex", shared.testCasesIndex) } }} open={isOpen} @@ -45,9 +43,14 @@ <Dialog.Portal> <Dialog.Overlay class="fixed inset-0 z-50 bg-gray-500 opacity-50 transition-opacity" + transition={fade} + transitionConfig={{ duration: 150 }} /> + <!-- TODO svelte5: transition --> <Dialog.Content class="fixed left-1/2 top-1/2 z-50 flex max-h-[85%] w-full max-w-6xl -translate-x-1/2 -translate-y-1/2 transform flex-col overflow-hidden rounded-md bg-white p-6 text-left shadow-xl transition-all" + transition={fade} + transitionConfig={{ duration: 200 }} > <Dialog.Title class="mb-4 text-2xl font-bold 2xl:text-3xl" >Choisir un cas type</Dialog.Title @@ -56,7 +59,7 @@ <TestCaseFilters {displayMode} on:select={({ detail }) => changeTestCaseIndex(detail)} - testCases={$testCases} + testCases={shared.testCases} variableName={displayMode.parametersVariableName} {year} /> diff --git a/src/lib/components/test_cases/TestCaseSummary.svelte b/src/lib/components/test_cases/TestCaseSummary.svelte index 2bbc50e39dd9c6f3572b5050fbe1c6ead4d9b7bd..e822733909184e9845fa487a0a18f6affabffb0b 100644 --- a/src/lib/components/test_cases/TestCaseSummary.svelte +++ b/src/lib/components/test_cases/TestCaseSummary.svelte @@ -7,8 +7,7 @@ ValueParameter, } from "@openfisca/json-model" import deepEqual from "deep-equal" - import { createEventDispatcher, getContext } from "svelte" - import type { Writable } from "svelte/store" + import { createEventDispatcher } from "svelte" import { page } from "$app/stores" import PictoBigAdulteRetraite from "$lib/components/pictos/PictoBigAdulteRetraite.svelte" @@ -27,6 +26,7 @@ import { entityByKey, personEntityKey } from "$lib/entities" import { getParameter, rootParameter } from "$lib/parameters" import publicConfig from "$lib/public_config" + import { billName } from "$lib/shared.svelte" import { getCalculatedVariableValueByCalculationName, getSituationVariableValue, @@ -58,9 +58,9 @@ childrenKey, familyEntityKey, householdEntityKey, - reformName, taxableHouseholdEntityKey, } = publicConfig + let { displayMode, mode, @@ -130,9 +130,9 @@ // Note: A reform variable is always more complete than a variable before reform. let variableSummary = $derived( - reformName === undefined + billName === undefined ? variableSummaryByName[displayMode.parametersVariableName] - : variableSummaryByNameByReformName[reformName][ + : variableSummaryByNameByReformName[billName][ displayMode.parametersVariableName ], ) diff --git a/src/lib/components/test_cases/TestCaseView.svelte b/src/lib/components/test_cases/TestCaseView.svelte index d4145d2291faebe776fc79857278e9f37a44fa74..429e0501092e2dfdfd5dba0fc8cedca96736d801 100644 --- a/src/lib/components/test_cases/TestCaseView.svelte +++ b/src/lib/components/test_cases/TestCaseView.svelte @@ -1,8 +1,4 @@ <script lang="ts"> - import type { Waterfall } from "@openfisca/json-model" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" - import { isNullVariableValueByCalculationName, variableValueByCalculationNameFromEvaluation, @@ -12,18 +8,12 @@ import OilSpendingBill from "$lib/components/test_cases/OilSpendingBill.svelte" import TestCaseSummary from "$lib/components/test_cases/TestCaseSummary.svelte" import TestCaseTab from "$lib/components/test_cases/TestCaseTab.svelte" - import Tooltip from "$lib/components/Tooltip.svelte" import VariableValueChange from "$lib/components/variables/VariableValueChange.svelte" import WaterfallView from "$lib/components/WaterfallView.svelte" - import type { - DecompositionByName, - EvaluationByName, - } from "$lib/decompositions" import { waterfalls } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" - import publicConfig from "$lib/public_config" - import type { ParametricReform } from "$lib/reforms" - import type { CalculationByName, Situation } from "$lib/situations" + import { billName, revaluationName, shared } from "$lib/shared.svelte" + import type { Situation } from "$lib/situations" import type { TabsConfig } from "$lib/tabs" import { newSimulationUrl } from "$lib/urls" import { @@ -34,7 +24,6 @@ } from "$lib/variables" interface Props { - decompositionByName: DecompositionByName displayMode: DisplayMode highlightDecomposition?: boolean situation: Situation @@ -44,9 +33,7 @@ year: number } - const { reformName, revaluationName } = publicConfig let { - decompositionByName, displayMode, highlightDecomposition = false, situation, @@ -65,19 +52,7 @@ tvaVariableName: `tva_sur_${name}`, })) - const calculationByName = getContext( - "calculationByName", - ) as Writable<CalculationByName> - const evaluationByNameArray = getContext("evaluationByNameArray") as Writable< - EvaluationByName[] - > - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const showNulls = getContext("showNulls") as Writable<boolean> - const waterfall = getContext("waterfall") as Writable<Waterfall> - - let evaluationByName = $derived($evaluationByNameArray[situationIndex]) + let evaluationByName = $derived(shared.evaluationByNameArray[situationIndex]) </script> <div @@ -137,11 +112,11 @@ {#if displayMode.parametersVariableName !== undefined} <!-- Note: A reform variable is always more complete than a variable before reform. --> {@const variableSummary = - reformName === undefined + billName === undefined ? variableSummaryByName[ displayMode.parametersVariableName ] - : variableSummaryByNameByReformName[reformName][ + : variableSummaryByNameByReformName[billName][ displayMode.parametersVariableName ]} {#if variableSummary !== undefined} @@ -170,11 +145,11 @@ {#if displayMode.parametersVariableName !== undefined} <!-- Note: A reform variable is always more complete than a variable before reform. --> {@const variableSummary = - reformName === undefined + billName === undefined ? variableSummaryByName[ displayMode.parametersVariableName ] - : variableSummaryByNameByReformName[reformName][ + : variableSummaryByNameByReformName[billName][ displayMode.parametersVariableName ]} {#if variableSummary !== undefined} @@ -184,22 +159,22 @@ variableValueByCalculationNameFromEvaluation( evaluationByName[name], revaluationName, - reformName, - $parametricReform, + billName, + shared.parametricReform, ), )} - {#if $showNulls || !linkedVariablesValueByCalculationName.every(isNullVariableValueByCalculationName)} + {#if shared.showNulls || !linkedVariablesValueByCalculationName.every(isNullVariableValueByCalculationName)} <ul class="flex flex-col gap-3"> {#each variableSummary.linked_added_variables as linkedVariableName, index} {@const linkedVariableValueByCalculationName = linkedVariablesValueByCalculationName[index]} - {#if $showNulls || !isNullVariableValueByCalculationName(linkedVariableValueByCalculationName)} + {#if shared.showNulls || !isNullVariableValueByCalculationName(linkedVariableValueByCalculationName)} {@const linkedVariableSummary = - reformName === undefined + billName === undefined ? variableSummaryByName[linkedVariableName] - : variableSummaryByNameByReformName[ - reformName - ][linkedVariableName]} + : variableSummaryByNameByReformName[billName][ + linkedVariableName + ]} <li> <a class="cursor-pointer text-sm hover:underline 2xl:text-base" @@ -235,9 +210,9 @@ {#if displayMode.parametersVariableName !== undefined} <!-- Note: A reform variable is always more complete than a variable before reform. --> {@const variableSummary = - reformName === undefined + billName === undefined ? variableSummaryByName[displayMode.parametersVariableName] - : variableSummaryByNameByReformName[reformName][ + : variableSummaryByNameByReformName[billName][ displayMode.parametersVariableName ]} {#if variableSummary !== undefined} @@ -267,11 +242,11 @@ variableValueByCalculationNameFromEvaluation( evaluationByName[name], revaluationName, - reformName, - $parametricReform, + billName, + shared.parametricReform, ), )} - {#if $showNulls || !linkedVariablesValueByCalculationName.every(isNullVariableValueByCalculationName)} + {#if shared.showNulls || !linkedVariablesValueByCalculationName.every(isNullVariableValueByCalculationName)} <ul class="flex flex-col gap-2 border-t-2 border-gray-200 pt-5 md:gap-5 lg:border-l-2 lg:border-t-0 lg:pl-5 lg:pt-0" class:lg:max-h-[200px]={allowColumnWrap} @@ -280,13 +255,13 @@ {#each linkedVariables as linkedVariableName, index} {@const linkedVariableValueByCalculationName = linkedVariablesValueByCalculationName[index]} - {#if $showNulls || !isNullVariableValueByCalculationName(linkedVariableValueByCalculationName)} + {#if shared.showNulls || !isNullVariableValueByCalculationName(linkedVariableValueByCalculationName)} {@const linkedVariableSummary = - reformName === undefined + billName === undefined ? variableSummaryByName[linkedVariableName] - : variableSummaryByNameByReformName[ - reformName - ][linkedVariableName]} + : variableSummaryByNameByReformName[billName][ + linkedVariableName + ]} <li class="flex"> <a class="cursor-pointer text-sm hover:underline 2xl:text-base" @@ -347,7 +322,7 @@ type="checkbox" value="" class="peer sr-only" - bind:checked={$showNulls} + bind:checked={shared.showNulls} /> <div class="peer relative h-6 w-11 shrink-0 rounded-full bg-gray-400 after:absolute after:start-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:bg-white after:transition-all after:content-[''] peer-checked:bg-le-vert-500 peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-0" @@ -376,9 +351,9 @@ <div class="bg-white"> {#if variableSummaryByName !== undefined} {@const completeVariableSummaryByName = - reformName === undefined + billName === undefined ? variableSummaryByName - : variableSummaryByNameByReformName[reformName]} + : variableSummaryByNameByReformName[billName]} <div class="flex w-full justify-between bg-gray-100"> <!--Impacts et waterfall--> <div @@ -386,11 +361,9 @@ id="situation_{situationIndex}_waterfall" > <WaterfallView - {decompositionByName} {displayMode} {evaluationByName} {highlightDecomposition} - on:changeDecompositionByName on:changeSituation {situation} {situationIndex} @@ -407,10 +380,10 @@ {#each waterfalls as { icon, label, name }} <a class="flex grow items-center justify-center shadow-inner" - class:bg-white={name === $waterfall.name} - class:border-le-bleu={name === $waterfall.name} - class:border-r-4={name === $waterfall.name} - class:shadow-none={name === $waterfall.name} + class:bg-white={name === shared.waterfall.name} + class:border-le-bleu={name === shared.waterfall.name} + class:border-r-4={name === shared.waterfall.name} + class:shadow-none={name === shared.waterfall.name} href={newSimulationUrl({ ...displayMode, waterfallName: name, @@ -428,8 +401,8 @@ {/if} <span class="ml-2 text-xs uppercase tracking-wide text-gray-600 xl:text-sm" - class:text-le-bleu={name === $waterfall.name} - class:font-bold={name === $waterfall.name} + class:text-le-bleu={name === shared.waterfall.name} + class:font-bold={name === shared.waterfall.name} > {label} </span> diff --git a/src/lib/components/variables/FormulaView.svelte b/src/lib/components/variables/FormulaView.svelte index c865d903c55f97bfd37bcc1c09965d954afc069e..61a2b690f5fd0951487fc6cc755e53039a6576be 100644 --- a/src/lib/components/variables/FormulaView.svelte +++ b/src/lib/components/variables/FormulaView.svelte @@ -11,6 +11,7 @@ import { entityByKey } from "$lib/entities" import { leafParametersName } from "$lib/parameters" import publicConfig from "$lib/public_config" + import { shared } from "$lib/shared.svelte" import type { Situation } from "$lib/situations" import { variableSummaryByName, @@ -40,9 +41,6 @@ }: Props = $props() const { openfiscaRepository } = publicConfig - const requestedVariablesNameToCalculate = getContext( - "requestedVariablesNameToCalculate", - ) as Writable<Set<string> | undefined> let extraction = $derived( extractFromFormulaAst( @@ -52,8 +50,8 @@ ), ) - run(() => { - $requestedVariablesNameToCalculate = extraction.openFiscaVariablesName + $effect(() => { + shared.requestedVariablesNameToCalculate = extraction.openFiscaVariablesName }) let openFiscaVariables = $derived( diff --git a/src/lib/components/variables/InflationLawButton.svelte b/src/lib/components/variables/InflationLawButton.svelte index a72f212e4a0d5c616f66d919764147a5d06b481f..e73089236246750d0e29f99c7b7b6463f8d86bcb 100644 --- a/src/lib/components/variables/InflationLawButton.svelte +++ b/src/lib/components/variables/InflationLawButton.svelte @@ -3,10 +3,8 @@ const bubble = createBubbler() import type { ValueParameter } from "@openfisca/json-model" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" - import publicConfig from "$lib/public_config" + import { billName, yearPLF } from "$lib/shared.svelte" import { formatValue } from "$lib/values" interface Props { @@ -14,11 +12,8 @@ parameterName: string | undefined } - const { reformName } = publicConfig let { inflatorByReformName, parameterName }: Props = $props() - const yearPLF = getContext("yearPLF") as Writable<number> - let inflatorWithLatestByReformName = $derived( Object.fromEntries( Object.entries(inflatorByReformName ?? []).map( @@ -47,9 +42,9 @@ // Latest inflator let billInflator = $derived( - reformName === undefined + billName === undefined ? undefined - : inflatorWithLatestByReformName?.[reformName], + : inflatorWithLatestByReformName?.[billName], ) let billInflatorValueFormatted = $derived( billInflator?.values !== undefined @@ -66,7 +61,7 @@ ></iconify-icon> <p class="text-left font-bold tracking-wider"> - Droit attendu pour {$yearPLF} + Droit attendu pour {yearPLF} <br /> <span class="text-sm font-normal tracking-wide"> {#if parameterName?.startsWith("impot_revenu.bareme_ir_depuis_1945.bareme")} diff --git a/src/lib/components/variables/InflationLawDetails.svelte b/src/lib/components/variables/InflationLawDetails.svelte index 250b89ed5c189e19475aa9951d065a3a18dd3130..140995bc378d28b400fba2ef77d2a4bbbd39c764 100644 --- a/src/lib/components/variables/InflationLawDetails.svelte +++ b/src/lib/components/variables/InflationLawDetails.svelte @@ -4,10 +4,8 @@ type ScaleParameter, type ValueParameter, } from "@openfisca/json-model" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" - import publicConfig from "$lib/public_config" + import { billName, revaluationName, yearPLF } from "$lib/shared.svelte" import { getUnitAtDate } from "$lib/units" import { formatValue, removeNegativeZero } from "$lib/values" @@ -18,7 +16,6 @@ revaluationParameter: ScaleParameter | ValueParameter } - const { revaluationName } = publicConfig let { billParameter, inflatorByReformName, @@ -30,7 +27,6 @@ new Intl.NumberFormat("fr-FR", { style: "decimal", }).format(removeNegativeZero(value)) - const yearPLF = getContext("yearPLF") as Writable<number> let parameterIsScale = $derived(billParameter.class === ParameterClass.Scale) @@ -155,7 +151,7 @@ : undefined, ) - let billInflator = $derived(inflatorWithLatestByReformName?.[reformName]) + let billInflator = $derived(inflatorWithLatestByReformName?.[billName]) let billInflatorValueFormatted = $derived( billInflator !== undefined ? formatValue(billInflator.values["latest"].value, billInflator.unit) @@ -176,13 +172,13 @@ <p class="mt-2 text-gray-600"> <span class="rounded-sm bg-gray-100 p-1 font-bold" >{lawValueFormatted} € - <span class="text-xs font-normal">{$yearPLF - 1}</span> + <span class="text-xs font-normal">{yearPLF - 1}</span> {#if showBillInflator} <span class="text-sm text-le-rouge-bill"> <iconify-icon class="align-[-0.3rem] text-xl" icon="mdi-arrow-right-bold" - ></iconify-icon>Le PLF {$yearPLF} augmente ce montant de + ></iconify-icon>Le PLF {yearPLF} augmente ce montant de <span class="text-base font-bold">{billInflatorValueFormatted}</span >. </span> @@ -195,7 +191,7 @@ ></iconify-icon></span ><span class="rounded-sm bg-gray-100 p-1 font-bold text-black" >{revaluationValueFormatted} € - <span class="text-xs font-normal">{$yearPLF}</span></span + <span class="text-xs font-normal">{yearPLF}</span></span > {/if} </span> @@ -211,7 +207,7 @@ <iconify-icon class="rotate-90 align-[-0.3rem] text-xl" icon="mdi-arrow-up-bold" - ></iconify-icon>Le PLF {$yearPLF} augmente les seuils de {billInflatorValueFormatted}. + ></iconify-icon>Le PLF {yearPLF} augmente les seuils de {billInflatorValueFormatted}. </p> {:else} <p @@ -220,7 +216,7 @@ <iconify-icon class="rotate-90 align-[-0.3rem] text-xl" icon="mdi-arrow-up-bold" - ></iconify-icon>En {$yearPLF}, les seuils augmentent de {revaluationInflatorValueFormatted}. + ></iconify-icon>En {yearPLF}, les seuils augmentent de {revaluationInflatorValueFormatted}. </p> {/if} {/if} @@ -247,7 +243,7 @@ {#if parameterName?.startsWith("impot_revenu.calcul_impot_revenu.plaf_qf.decote.seuil_couple") || parameterName?.startsWith("impot_revenu.calcul_impot_revenu.plaf_qf.decote.seuil_celib")} <p class="font-serif text-lg"> - En {$yearPLF}, il est attendu que les + En {yearPLF}, il est attendu que les <strong>seuils de la décote soient revalorisés.</strong> </p> @@ -271,7 +267,7 @@ {#if parameterName?.startsWith("impot_revenu.calcul_impot_revenu.plaf_qf.plafond_avantages_procures_par_demi_part.general") || parameterName?.startsWith("impot_revenu.calcul_impot_revenu.plaf_qf.plafond_avantages_procures_par_demi_part.celib")} <p class="font-serif text-lg"> - En {$yearPLF}, il est attendu que + En {yearPLF}, il est attendu que <strong >les paramètres de plafonnement des effets du quotient familial sur l'impôt sur le revenu soient revalorisés.</strong @@ -298,7 +294,7 @@ {#if parameterName?.startsWith("impot_revenu.calcul_impot_revenu.plaf_qf.plafond_avantages_procures_par_demi_part.reduc_postplafond")} <p class="font-serif text-lg"> - En {$yearPLF}, il est attendu que + En {yearPLF}, il est attendu que <strong >les réductions d'impôt pour invalides, anciens combattants ou veufs avec enfants à charge, intervenant si le plafonnement a été atteint, soient @@ -326,7 +322,7 @@ {#if parameterName?.startsWith("impot_revenu.calcul_revenus_imposables.abat_rni.enfant_marie.montant")} <p class="font-serif text-lg"> - En {$yearPLF}, il est attendu que + En {yearPLF}, il est attendu que <strong >l'abattement spécial pour enfants mariés à charge soit revalorisé.</strong > @@ -352,7 +348,7 @@ {#if parameterName?.startsWith("impot_revenu.calcul_reductions_impots.dons.dons_coluche.plafond")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant maximum de dons dits « Coluche » suivra l'augmentation appliquée par le PLF sur le seuil de la première tranche du barème de @@ -379,7 +375,7 @@ {#if parameterName?.startsWith("impot_revenu.calcul_revenus_imposables.abat_rni.personne_agee_ou_invalide")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >l'abattement spécial pour personne âgée ou invalide suivra l'augmentation appliquée par le PLF sur le seuil de la première tranche du barème de @@ -406,7 +402,7 @@ {#if parameterName?.startsWith("impot_revenu.calcul_revenus_imposables.charges_deductibles.accueil_personne_agee.plafond")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant maximum de déduction de charges pour accueil d'une personne âgée suivra l'augmentation appliquée par le PLF sur le seuil de la @@ -433,7 +429,7 @@ {#if parameterName?.startsWith("impot_revenu.calcul_revenus_imposables.deductions.abatpen")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant maximum de l'abattement sur les retraites suivra l'augmentation appliquée par le PLF sur le seuil de la première tranche du @@ -460,7 +456,7 @@ {#if parameterName?.startsWith("impot_revenu.calcul_revenus_imposables.deductions.abatpro")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant maximum de l'abattement suivra l'augmentation appliquée par le PLF sur le seuil de la première tranche du barème de l'impôt sur le @@ -502,7 +498,7 @@ {#if parameterName?.startsWith("impot_revenu.calcul_revenus_imposables.charges_deductibles.pensions_alimentaires.plafond")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant maximum de déduction par enfant de la pension alimentaire suivra l'augmentation appliquée par le PLF sur le seuil de la première @@ -521,7 +517,7 @@ {#if parameterName?.startsWith("prelevements_sociaux.contributions_sociales.csg.remplacement.seuils")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >les seuils de RFR déterminant le taux de CSG sont revalorisés de {revaluationInflatorValueFormatted}.</strong > @@ -544,7 +540,7 @@ {#if parameterName?.startsWith("prestations_sociales.aides_logement.allocations_logement.al_loc2.par_zone")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >les plafonds des loyers sont revalorisés de {revaluationInflatorValueFormatted}.</strong > @@ -566,7 +562,7 @@ {#if parameterName?.startsWith("prestations_sociales.aides_logement.allocations_logement.al_charge")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant forfaitaire des charges est revalorisé de {revaluationInflatorValueFormatted}.</strong > @@ -588,7 +584,7 @@ {#if parameterName?.startsWith("prestations_sociales.aides_logement.allocations_logement.al_etudiant")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >les équivalences de loyer et de charges locatives sont revalorisées de {revaluationInflatorValueFormatted}.</strong > @@ -610,7 +606,7 @@ {#if parameterName?.startsWith("prestations_sociales.aides_logement.allocations_logement.al_plaf_logement_foyer")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >les équivalences de loyer et de charges locatives sont revalorisées de {revaluationInflatorValueFormatted}.</strong > @@ -632,7 +628,7 @@ {#if parameterName?.startsWith("prestations_sociales.aides_logement.allocations_logement.al_loc2.montant_forfaitaire_participation_minimale_po")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le terme constant « P0 » de la participation personnelle du ménage est revalorisé de {revaluationInflatorValueFormatted}.</strong @@ -655,7 +651,7 @@ {#if parameterName?.startsWith("prestations_sociales.aides_logement.allocations_logement.al_param_r0.r0")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le paramètre de calcul des ressources « R0 » est revalorisé de {revaluationInflatorValueFormatted}.</strong > @@ -679,7 +675,7 @@ {#if parameterName?.startsWith("prestations_sociales.aides_logement.ressources.dar_4") || parameterName?.startsWith("prestations_sociales.aides_logement.ressources.dar_5") || parameterName?.startsWith("prestations_sociales.aides_logement.ressources.dar_11") || parameterName?.startsWith("prestations_sociales.aides_logement.ressources.dar_12")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le forfait de ressources pour les étudiants est revalorisé de {revaluationInflatorValueFormatted}.</strong > @@ -702,7 +698,7 @@ {#if parameterName?.startsWith("prestations_sociales.solidarite_insertion.minima_sociaux.ppa.pa_m.montant_de_base")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant de base de la prime d'activité est revalorisé de {revaluationInflatorValueFormatted}.</strong > @@ -733,7 +729,7 @@ {#if parameterName?.startsWith("prestations_sociales.solidarite_insertion.minima_sociaux.rsa.rsa_m.montant_de_base_du_rsa")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant de base du RSA est revalorisé de {revaluationInflatorValueFormatted}.</strong > @@ -764,7 +760,7 @@ {#if parameterName?.startsWith("prestations_sociales.solidarite_insertion.minimum_vieillesse.aspa.plafond_ressources") || parameterName?.startsWith("prestations_sociales.solidarite_insertion.minimum_vieillesse.aspa.montant_maximum_annuel")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant et le plafond de ressources de l'ASPA sont revalorisés de {revaluationInflatorValueFormatted}.</strong > @@ -795,7 +791,7 @@ {#if parameterName?.startsWith("prestations_sociales.prestations_etat_de_sante.invalidite.asi.plafond_ressource_couple") || parameterName?.startsWith("prestations_sociales.prestations_etat_de_sante.invalidite.asi.plafond_ressource_seul")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant et le plafond de ressources de l'ASI sont revalorisés de {revaluationInflatorValueFormatted}.</strong > @@ -826,7 +822,7 @@ {#if parameterName?.startsWith("prestations_sociales.aides_jeunes.contrat_engagement_jeune.montants")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >les montants du contrat d'engagement jeune sont revalorisés de {revaluationInflatorValueFormatted}.</strong > @@ -857,7 +853,7 @@ {#if parameterName?.startsWith("prestations_sociales.prestations_etat_de_sante.invalidite.aah.montant")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant maximal mensuel de l'AAH est revalorisé de {revaluationInflatorValueFormatted}.</strong > @@ -888,7 +884,7 @@ {#if parameterName?.startsWith("prestations_sociales.prestations_familiales.bmaf.bmaf")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le montant de la base mensuelle des allocations familiales est revalorisé de {revaluationInflatorValueFormatted}.</strong @@ -920,7 +916,7 @@ {#if parameterName?.startsWith("prelevements_sociaux.pss")} <p class="font-serif text-lg"> - En {$yearPLF}, + En {yearPLF}, <strong >le plafond de la sécurité sociale est revalorisé de {revaluationInflatorValueFormatted}.</strong > diff --git a/src/lib/components/variables/InflationLawInfoModal.svelte b/src/lib/components/variables/InflationLawInfoModal.svelte index c17d7fc0031c9bd05da48a6bb66c38de3da21a72..ed0ea5314b0964f2ae136017f4f802bb85b6ceeb 100644 --- a/src/lib/components/variables/InflationLawInfoModal.svelte +++ b/src/lib/components/variables/InflationLawInfoModal.svelte @@ -5,12 +5,10 @@ Reference, } from "@openfisca/json-model" import { Dialog } from "bits-ui" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import ArticleModal from "$lib/components/parameters/ArticleModal.svelte" import InflationLawDetails from "$lib/components/variables/InflationLawDetails.svelte" - import publicConfig from "$lib/public_config" + import { billName, yearPLF } from "$lib/shared.svelte" interface Props { isOpen?: boolean @@ -20,7 +18,6 @@ revaluationParameter: ScaleParameter | ValueParameter } - const { reformName } = publicConfig let { isOpen = $bindable(false), billParameter, @@ -30,11 +27,10 @@ }: Props = $props() let openReferenceUrl: string | undefined | null = $state(null) - const yearPLF = getContext("yearPLF") as Writable<number> // Latest inflator, contains all references. let billInflator = $derived( - reformName === undefined ? undefined : inflatorByReformName?.[reformName], + billName === undefined ? undefined : inflatorByReformName?.[billName], ) let references = $derived( @@ -70,7 +66,7 @@ <Dialog.Title class="mb-8 w-full text-center text-2xl font-bold md:text-3xl" > - Droit attendu {$yearPLF} + Droit attendu {yearPLF} <!-- <div class="text-sm">{billParameter.name}</div>--> </Dialog.Title> @@ -88,11 +84,11 @@ class="rounded-md bg-le-gris-dispositif-ultralight p-4 text-le-gris-dispositif-dark" > <p class="mb-2 text-lg font-bold"> - Qu'est-ce que le droit attendu pour {$yearPLF} ? + Qu'est-ce que le droit attendu pour {yearPLF} ? </p> <p class="text-base leading-6"> En cette période budgétaire, le simulateur LexImpact se projette - en {$yearPLF} pour vous permettre de comprendre et d'évaluer les projets + en {yearPLF} pour vous permettre de comprendre et d'évaluer les projets de loi de finances (PLF) et de financement de la sécurité sociale (PLFSS).<br /><br /> <span class="font-bold" @@ -101,20 +97,20 @@ </p> <ul class="list-inside list-disc"> <li class="py-2"> - Le droit attendu en {$yearPLF} et ses impacts + Le droit attendu en {yearPLF} et ses impacts <span class="italic" - >(prenant en compte les revalorisations automatiques pour {$yearPLF} + >(prenant en compte les revalorisations automatiques pour {yearPLF} explicitées dans la loi)</span - >. Lorsque le droit attendu en {$yearPLF} est différent du droit - en vigueur en {$yearPLF - 1}, ce dernier est spécifié en petit - au dessus de la valeur {$yearPLF} ; + >. Lorsque le droit attendu en {yearPLF} est différent du droit en + vigueur en {yearPLF - 1}, ce dernier est spécifié en petit au + dessus de la valeur {yearPLF} ; </li> <li class="py-2"> - Les PLF {$yearPLF} & PLFSS {$yearPLF}, ainsi que leurs + Les PLF {yearPLF} & PLFSS {yearPLF}, ainsi que leurs impacts ; </li> <li class="py-2"> - Votre réforme pour l'année {$yearPLF} et ses impacts. + Votre réforme pour l'année {yearPLF} et ses impacts. </li> </ul> </div> diff --git a/src/lib/components/variables/VariableDetail.svelte b/src/lib/components/variables/VariableDetail.svelte index 6483910fa65ccfa9f883a3c86c79daf042f8445d..b6386e0aa839219db29d9818c2f9f362b76a597e 100644 --- a/src/lib/components/variables/VariableDetail.svelte +++ b/src/lib/components/variables/VariableDetail.svelte @@ -1,17 +1,12 @@ <script lang="ts"> import { run } from "svelte/legacy" - import { createEventDispatcher, getContext } from "svelte" + import { createEventDispatcher } from "svelte" import { quartOut } from "svelte/easing" - import type { Writable } from "svelte/store" import { fade, fly } from "svelte/transition" import { goto } from "$app/navigation" - import type { BudgetSimulation } from "$lib/budgets" - import { - requestAllBudgetCalculations, - type RequestedCalculations, - } from "$lib/calculations.svelte" + import { requestAllBudgetCalculations } from "$lib/calculations.svelte" import { clickOutside } from "$lib/click_outside" import Accordion from "$lib/components/accordion/Accordion.svelte" import AccordionItem from "$lib/components/accordion/AccordionItem.svelte" @@ -23,13 +18,11 @@ import { decompositionCoreByName, decompositionCoreByNameByReformName, - type EvaluationByName, getLatestCalculation, } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" import { memoUrlByName } from "$lib/memos" - import publicConfig from "$lib/public_config" - import type { Situation } from "$lib/situations" + import { billName, shared, year } from "$lib/shared.svelte" import { newSimulationUrl } from "$lib/urls" import { budgetVariablesName, @@ -42,23 +35,14 @@ name: string } - const { reformName } = publicConfig let { displayMode, name }: Props = $props() - const budgetSimulation = getContext("budgetSimulation") as Writable< - BudgetSimulation | undefined - > let budgetSimulationOutdated = $state(false) const dateFormatter = new Intl.DateTimeFormat("fr-FR", { dateStyle: "medium", }).format const dispatch = createEventDispatcher() - const evaluationByNameArray = getContext("evaluationByNameArray") as Writable< - EvaluationByName[] - > let innerHeight: number = $state() - const testCases = getContext("testCases") as Writable<Situation[]> - const year = getContext("year") as Writable<number> function requestBudgetCalculations() { if (budgetVariablesName.has(name)) { @@ -70,7 +54,7 @@ // before reform. // → Non reform decomposition is not needed. let latestDecompositionCoreByName = $derived( - decompositionCoreByNameByReformName[reformName] ?? decompositionCoreByName, + decompositionCoreByNameByReformName[billName] ?? decompositionCoreByName, ) let latestDecompositionCore = $derived(latestDecompositionCoreByName[name]) let decomposition = $derived( @@ -84,7 +68,7 @@ // Note: A reform variable is always more complete than a variable before reform. // But it may contain different formulas, with different parameters & variables. let latestVariableSummaryByName = $derived( - variableSummaryByNameByReformName[reformName] ?? variableSummaryByName, + variableSummaryByNameByReformName[billName] ?? variableSummaryByName, ) let variable = $derived(latestVariableSummaryByName[name]) run(() => { @@ -113,7 +97,7 @@ decomposition?.reference ?? variable?.reference, ) let indexedTestCases = $derived( - $testCases + shared.testCases .map((situation, situationIndex) => ({ situation, situationIndex })) .filter( ({ situation }) => situation.linked_variables?.[name] !== undefined, @@ -151,9 +135,9 @@ ), ) let nonNullTestCases = $derived( - $testCases.reduce((arr, curr, index) => { + shared.testCases.reduce((arr, curr, index) => { const latestCalculationValue = getLatestCalculation( - $evaluationByNameArray[index][name]?.calculationEvaluationByName, + shared.evaluationByNameArray[index][name]?.calculationEvaluationByName, )?.deltaAtVectorIndex if (latestCalculationValue !== undefined && latestCalculationValue > 0) { const [titleBoldPart, titleRegularPart] = curr.title.split("|") @@ -318,19 +302,19 @@ title="Impacts budgétaires" > {#if budgetVariablesName.has(name)} - {#if budgetSimulationOutdated || $budgetSimulation === undefined} + {#if budgetSimulationOutdated || shared.budgetSimulation === undefined} <div class="flex animate-pulse-2 flex-col gap-2"> <div class="mr-64 h-6 self-stretch bg-neutral-300"></div> <div class="mr-48 h-6 self-stretch bg-neutral-300"></div> </div> - {:else if $budgetSimulation.errors != null && $budgetSimulation.errors.length > 0} - {#each $budgetSimulation.errors as error} + {:else if shared.budgetSimulation.errors != null && shared.budgetSimulation.errors.length > 0} + {#each shared.budgetSimulation.errors as error} <span class="text-base">{error}</span> {/each} {:else} <span class="text-base xl:text-lg"> <VariableDetailBudget - budgetSimulation={$budgetSimulation} + budgetSimulation={shared.budgetSimulation} {name} /></span > @@ -412,7 +396,7 @@ <TestCasePictos classes="[&>svg]:w-7 [&>svg]:h-7 col-span-3 last:odd:col-start-3 justify-center" situation={situations[0]} - year={$year} + {year} /> </div> {/if} @@ -435,7 +419,7 @@ {/if} <VariableValueChange bold={true} - evaluationByName={$evaluationByNameArray[index]} + evaluationByName={shared.evaluationByNameArray[index]} inline {name} /> @@ -467,9 +451,9 @@ }), )} showOnlyDeciles={false} - testCases={$testCases} + testCases={shared.testCases} variableName={name} - year={$year} + {year} /> </AccordionItem> </Accordion> diff --git a/src/lib/components/variables/VariableDetailBudget.svelte b/src/lib/components/variables/VariableDetailBudget.svelte index aaff8adec2ae83510700853ee2bd8ac212ad6943..8404377cdea1db12cd6f8a2cadb3885b69d9320e 100644 --- a/src/lib/components/variables/VariableDetailBudget.svelte +++ b/src/lib/components/variables/VariableDetailBudget.svelte @@ -4,7 +4,6 @@ import type { BudgetSimulation } from "$lib/budgets" import ValueChange from "$lib/components/ValueChange.svelte" - import type { DecompositionByName } from "$lib/decompositions" import { type BudgetVariable, budgetVariableNameByVariableName, @@ -12,6 +11,7 @@ variableSummaryByName, type VariableValueByCalculationName, } from "$lib/variables" + import { shared } from "$lib/shared.svelte" interface Props { budgetSimulation: BudgetSimulation @@ -20,10 +20,6 @@ let { budgetSimulation, name }: Props = $props() - const decompositionByName = getContext( - "decompositionByName", - ) as Writable<DecompositionByName> - function buildBudgetValueByCalculationName( type: string, variableName: string, @@ -94,9 +90,9 @@ let budgetVariable = $derived({ ...budgetVariablesConfig[budgetVariableName], label: - $decompositionByName[budgetVariableName]?.short_label ?? + shared.decompositionByName[budgetVariableName]?.short_label ?? variableSummaryByName[budgetVariableName].short_label ?? - $decompositionByName[budgetVariableName]?.label ?? + shared.decompositionByName[budgetVariableName]?.label ?? variableSummaryByName[budgetVariableName].label, name: budgetVariableName, } as BudgetVariable & { label: string; name: string }) diff --git a/src/lib/components/variables/VariableReferredInputsPane.svelte b/src/lib/components/variables/VariableReferredInputsPane.svelte index 4afa8a22b172896eccd0d8a2908f94677cc297fb..0089edac87203a39627916bea74f6249690b8191 100644 --- a/src/lib/components/variables/VariableReferredInputsPane.svelte +++ b/src/lib/components/variables/VariableReferredInputsPane.svelte @@ -1,9 +1,6 @@ <script lang="ts"> - import { run } from "svelte/legacy" - - import type { Variable, Waterfall } from "@openfisca/json-model" - import { createEventDispatcher, getContext } from "svelte" - import type { Writable } from "svelte/store" + import type { Variable } from "@openfisca/json-model" + import { createEventDispatcher } from "svelte" import VariableReferredInputs from "./VariableReferredInputs.svelte" @@ -17,6 +14,7 @@ iterVariableInputVariables, variableSummaryByName, } from "$lib/variables" + import { shared } from "$lib/shared.svelte" interface Props { date: string @@ -43,14 +41,13 @@ let currentInputInstantsByVariableName = inputInstantsByVariableName let currentSituation = situation const dispatch = createEventDispatcher() - const waterfall = getContext("waterfall") as Writable<Waterfall> function getVariableReferredInputs(name: string, date: string): Variable[] { const ignoreVariablesName = new Set( walkDecompositionsCoreName( decompositionCoreByName, - $waterfall.name, - $waterfall.root, + shared.waterfall.name, + shared.waterfall.root, false, ), ) @@ -76,16 +73,16 @@ dispatch("changeSituation", situation) } let variable = $derived(variableSummaryByName[name]) - run(() => { + $effect(() => { if (variable === undefined) { console.error(`Variable "${name}" not found`) } }) let inputs = $derived(getVariableReferredInputs(name, date)) - run(() => { + $effect(() => { updateInputInstantsByVariableName(inputInstantsByVariableName) }) - run(() => { + $effect(() => { updateSituation(situation) }) </script> diff --git a/src/lib/components/variables/VariableReferredParameters.svelte b/src/lib/components/variables/VariableReferredParameters.svelte index 88dc01c1bf91fb17a34c7bee4ea607891bd4b8ab..3cec3008449b8d394237b83c1095311ff9377580 100644 --- a/src/lib/components/variables/VariableReferredParameters.svelte +++ b/src/lib/components/variables/VariableReferredParameters.svelte @@ -8,8 +8,6 @@ type NodeParameter, type Parameter, } from "@openfisca/json-model" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import VariableHeader from "./VariableHeader.svelte" import VariableReferredNodeParameter from "./VariableReferredNodeParameter.svelte" @@ -29,7 +27,7 @@ rootParameter, rootParameterByReformName, } from "$lib/parameters" - import publicConfig from "$lib/public_config" + import { billActive, billName, revaluationName } from "$lib/shared.svelte" import { newSimulationUrl } from "$lib/urls" import { iterVariableParametersName, @@ -43,17 +41,49 @@ name: string } - const { reformName, revaluationName } = publicConfig let { date, displayMode, name }: Props = $props() - const billActive = getContext("billActive") as Writable<boolean> let openDirectParameters = $state(true) + + function* iterVariableRootParameters( + bothRootParameterById: { [id: string]: Parameter }, + lawRootParameter: NodeParameter, + revaluationRootParameter: NodeParameter, + billRootParameter: NodeParameter, + ): Generator< + [Parameter, Parameter | undefined, Parameter, Parameter], + void, + unknown + > { + for (const bothChild of Object.values(bothRootParameterById).sort( + (bothChild1, bothChild2) => + bothChild1.class === bothChild2.class || + (bothChild1.class !== ParameterClass.Node && + bothChild2.class !== ParameterClass.Node) + ? bothChild1.title.localeCompare(bothChild2.title) + : bothChild1.class === ParameterClass.Node + ? 1 + : -1, + )) { + if (bothChild.id === undefined) { + // TODO: This is a mistake that shoudn't occur. + continue + } + // Notes: + // A reform (bill) parameter is always more complete than a parameter before reform. + // A reform never changes the class of a parameter + const lawChild = lawRootParameter.children[bothChild.id] // Sometimes undefined + const revaluationChild = revaluationRootParameter.children[bothChild.id] + const billChild = billRootParameter.children[bothChild.id] // Never undefined + yield [bothChild, lawChild, revaluationChild, billChild] + } + } // Note: A reform decomposition is always more complete than a decomposition before reform. // And the children of a reform decomposition always contain the children of the decomposition // before reform. // => Non reform decomposition is not needed. let billDecompositionCoreByName = $derived( - decompositionCoreByNameByReformName[reformName] ?? decompositionCoreByName, + decompositionCoreByNameByReformName[billName] ?? decompositionCoreByName, ) let billDecompositionCore = $derived(billDecompositionCoreByName[name]) let billDecomposition = $derived( @@ -67,7 +97,7 @@ // Note: A reform variable is always more complete than a variable before reform. // But it may contain different formulas, with different parameters & variables. let billVariableSummaryByName = $derived( - variableSummaryByNameByReformName[reformName] ?? variableSummaryByName, + variableSummaryByNameByReformName[billName] ?? variableSummaryByName, ) let billVariable = $derived(billVariableSummaryByName[name]) let lawVariable = $derived(variableSummaryByName[name]) @@ -83,7 +113,7 @@ // And the children of a reform node parameter always contain the children of the node parameter // before reform (albeit with some different value parameters). let billRootParameter = $derived( - rootParameterByReformName[reformName] ?? revaluationRootParameter, + rootParameterByReformName[billName] ?? revaluationRootParameter, ) let bothParametersName = $derived( new Set([ @@ -92,7 +122,7 @@ : iterVariableParametersName(lawVariable, date)), ...(billVariable === undefined ? [] - : iterVariableParametersName(billVariable, date, reformName)), + : iterVariableParametersName(billVariable, date, billName)), ]), ) let bothDirectParametersName = $derived( @@ -120,40 +150,6 @@ ), ) let openAllParameters = $state(false) - - function* iterVariableRootParameters( - bothRootParameterById: { [id: string]: Parameter }, - lawRootParameter: NodeParameter, - revaluationRootParameter: NodeParameter, - billRootParameter: NodeParameter, - ): Generator< - [Parameter, Parameter | undefined, Parameter, Parameter], - void, - unknown - > { - for (const bothChild of Object.values(bothRootParameterById).sort( - (bothChild1, bothChild2) => - bothChild1.class === bothChild2.class || - (bothChild1.class !== ParameterClass.Node && - bothChild2.class !== ParameterClass.Node) - ? bothChild1.title.localeCompare(bothChild2.title) - : bothChild1.class === ParameterClass.Node - ? 1 - : -1, - )) { - if (bothChild.id === undefined) { - // TODO: This is a mistake that shoudn't occur. - continue - } - // Notes: - // A reform (bill) parameter is always more complete than a parameter before reform. - // A reform never changes the class of a parameter - const lawChild = lawRootParameter.children[bothChild.id] // Sometimes undefined - const revaluationChild = revaluationRootParameter.children[bothChild.id] - const billChild = billRootParameter.children[bothChild.id] // Never undefined - yield [bothChild, lawChild, revaluationChild, billChild] - } - } </script> {#if billDecomposition !== undefined || billVariable !== undefined} @@ -165,7 +161,7 @@ /> </div> <!--PLFSS 2025 - Affichage spécifique pour la réduction générale --> - {#if $billActive} + {#if billActive} {#if billVariable.name === "allegement_general"} <div class="mx-4 mb-4 rounded-sm bg-red-100 p-1 text-sm"> <span class="font-bold text-le-rouge-bill" diff --git a/src/lib/components/variables/VariableReferredScaleParameter.svelte b/src/lib/components/variables/VariableReferredScaleParameter.svelte index 3b5647488cf8538c09420b5bb9986b4fc6b8ff01..1556cca2eadbaec26244e537743c20011c74de25 100644 --- a/src/lib/components/variables/VariableReferredScaleParameter.svelte +++ b/src/lib/components/variables/VariableReferredScaleParameter.svelte @@ -9,14 +9,11 @@ type ValueParameter, } from "@openfisca/json-model" import deepEqual from "deep-equal" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import { goto } from "$app/navigation" import { requestBudgetCalculation, requestTestCasesCalculation, - type RequestedCalculations, } from "$lib/calculations.svelte" import ArticleModal from "$lib/components/parameters/ArticleModal.svelte" import InflationLawButton from "$lib/components/variables/InflationLawButton.svelte" @@ -30,12 +27,17 @@ trackLawEditParameterStatus, } from "$lib/matomo" import { rootParameterByReformName } from "$lib/parameters" - import publicConfig from "$lib/public_config" import { ParameterReformChangeType, - type ParametricReform, type ScaleParameterReform, } from "$lib/reforms" + import { + billActive, + billName, + revaluationName, + shared, + yearPLF, + } from "$lib/shared.svelte" import { budgetEditableParametersNameByVariableName, budgetVariablesName, @@ -52,7 +54,6 @@ revaluationParameter: ScaleParameter } - const { reformName, revaluationName } = publicConfig let { billParameter, budget, @@ -64,15 +65,10 @@ revaluationParameter, }: Props = $props() - const billActive = getContext("billActive") as Writable<boolean> const dateFormatter = new Intl.DateTimeFormat("fr-FR", { dateStyle: "full" }) .format let isInflationLawInfoModalOpen = $state(false) let openReferenceUrl: string | undefined | null = $state(null) - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const yearPLF = getContext("yearPLF") as Writable<number> function changeScale({ detail: scale }: { detail: ScaleAtInstant }) { updateReform(`${date.split("-")[0]}-01-01`, scale) @@ -100,18 +96,15 @@ scale !== undefined && Object.keys(scale).length > 0 ) { - $parametricReform = { - ...$parametricReform, - [billParameter.name as string]: { - scale, - start, - type: ParameterReformChangeType.Scale, - variable: displayMode.parametersVariableName, - }, + shared.parametricReform[billParameter.name as string] = { + scale, + start, + type: ParameterReformChangeType.Scale, + variable: displayMode.parametersVariableName, } if (deepEqual(scale, billScaleAtInstant)) { - delete $parametricReform[billParameter.name] + delete shared.parametricReform[billParameter.name] } if (name !== undefined && budgetVariablesName.has(name)) { @@ -121,7 +114,7 @@ } } let change = $derived( - $parametricReform[billParameter.name as string] as + shared.parametricReform[billParameter.name as string] as | ScaleParameterReform | undefined, ) @@ -220,7 +213,7 @@ inflatorType![1] ], ...{ - inflator_reference: (reformName === reformName + inflator_reference: (reformName === billName ? billParameter : reformName === revaluationName ? revaluationParameter @@ -254,15 +247,15 @@ <div class="ml-8 flex pt-2 text-xs italic"> <p class="w-20 text-center"> {#if showRevaluationLabel} - <span class="text-[0.6rem]">Droit {$yearPLF - 1}</span> + <span class="text-[0.6rem]">Droit {yearPLF - 1}</span> <br /> {/if} <span - title="Droit attendu en {$yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {$yearPLF}." + title="Droit attendu en {yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {yearPLF}." class="font-bold underline decoration-dotted" > - {#if $billActive} - Droit {$yearPLF} <br />sans PLF/PLSS + {#if billActive} + Droit {yearPLF} <br />sans PLF/PLSS {:else} Droit en vigueur {/if}</span @@ -270,13 +263,13 @@ </p> {#if showBillLabel} <p class="w-20 text-center font-bold text-le-rouge-bill"> - PLF/PLFSS<br />{$yearPLF} + PLF/PLFSS<br />{yearPLF} </p> {/if} {#if showParametricReformLabel} <p class="w-20 text-center font-bold"> <span class="bg-le-jaune px-2">Votre réforme</span><br /><span - class="bg-le-jaune px-2">pour {$yearPLF}</span + class="bg-le-jaune px-2">pour {yearPLF}</span > </p> {/if} diff --git a/src/lib/components/variables/VariableReferredValueParameter.svelte b/src/lib/components/variables/VariableReferredValueParameter.svelte index e1318d4e78e9f1e9d155f063121ddf87d456c4b2..cce1d0dbff3b5b5e39e230a6999c9f2560c7201f 100644 --- a/src/lib/components/variables/VariableReferredValueParameter.svelte +++ b/src/lib/components/variables/VariableReferredValueParameter.svelte @@ -17,14 +17,11 @@ type ValueAtInstant, type ValueParameter, } from "@openfisca/json-model" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import { goto } from "$app/navigation" import { requestBudgetCalculation, requestTestCasesCalculation, - type RequestedCalculations, } from "$lib/calculations.svelte" import ArticleModal from "$lib/components/parameters/ArticleModal.svelte" import InflationLawButton from "$lib/components/variables/InflationLawButton.svelte" @@ -39,12 +36,17 @@ trackLawEditParameterStatus, } from "$lib/matomo" import { rootParameterByReformName } from "$lib/parameters" - import publicConfig from "$lib/public_config" import { ParameterReformChangeType, - type ParametricReform, type ValueParameterReform, } from "$lib/reforms" + import { + billActive, + billName, + revaluationName, + shared, + yearPLF, + } from "$lib/shared.svelte" import { getUnitAtDate, getUnitShortLabel } from "$lib/units" import { budgetEditableParametersNameByVariableName, @@ -63,7 +65,6 @@ revaluationParameter: ValueParameter } - const { reformName, revaluationName } = publicConfig let { billParameter, budget, @@ -76,7 +77,6 @@ revaluationParameter, }: Props = $props() - const billActive = getContext("billActive") as Writable<boolean> let billLatestInstantValueCouplesArray: [string, ValueAtInstant][] = $derived( Object.entries(billParameter.values).sort(([instant1], [instant2]) => instant2.localeCompare(instant1), @@ -91,9 +91,6 @@ ), ) let openReferenceUrl: string | null = $state(null) - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> let revaluationLatestInstantValueCouplesArray: [string, ValueAtInstant][] = $derived( Object.entries(revaluationParameter?.values ?? []).sort( @@ -102,7 +99,6 @@ ) let validValue = undefined let valueError: string | null = $state(null) - const yearPLF = getContext("yearPLF") as Writable<number> function changeValue({ detail: value }: CustomEvent) { ;[validValue, valueError] = auditChain( @@ -140,7 +136,7 @@ function updateReform(start: string, value: number | undefined) { if (start !== undefined && value !== undefined) { const updatedParametricReform = { - ...$parametricReform, + ...shared.parametricReform, } if (value === billValue.value) { delete updatedParametricReform[billParameter.name as string] @@ -152,7 +148,7 @@ value, } } - $parametricReform = updatedParametricReform + shared.parametricReform = updatedParametricReform if (name !== undefined && budgetVariablesName.has(name)) { requestBudgetCalculation(name, "amendment") @@ -170,7 +166,7 @@ let change = $derived( billParameter.name === undefined ? undefined - : ($parametricReform[billParameter.name] as + : (shared.parametricReform[billParameter.name] as | ValueParameterReform | undefined), ) @@ -222,7 +218,7 @@ inflatorType![1] ], ...{ - inflator_reference: (reformName === reformName + inflator_reference: (reformName === billName ? billParameter : reformName === revaluationName ? revaluationParameter @@ -306,15 +302,15 @@ > <p class="w-20 text-center"> {#if isRevaluationActive} - <span class="text-[0.6rem]">Droit {$yearPLF - 1}</span> + <span class="text-[0.6rem]">Droit {yearPLF - 1}</span> <br /> {/if} <span - title="Droit attendu en {$yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {$yearPLF}." + title="Droit attendu en {yearPLF} suite aux revalorisations automatiques prévues dans la loi, sans les modifications qui seront apportées par le PLF et le PLFSS {yearPLF}." class="font-bold underline decoration-dotted" > - {#if $billActive} - Droit {$yearPLF} <br />sans PLF/PLSS + {#if billActive} + Droit {yearPLF} <br />sans PLF/PLSS {:else} Droit en vigueur {/if}</span @@ -322,7 +318,7 @@ </p> {#if isBillActive} <p class="w-20 text-center font-bold text-le-rouge-bill"> - PLF/PLFSS {$yearPLF} + PLF/PLFSS {yearPLF} </p> {/if} {#if isParametricReformActive} @@ -350,7 +346,7 @@ {/if} </div> <!--Spécial PLF 2025 : Alerte paramètre modifié par le PLF mais pas modifié par LexImpact --> - {#if $billActive && billParameter.name?.startsWith("prelevements_sociaux.reductions_cotisations_sociales.allegement_general.ensemble_des_entreprises.plafond")} + {#if billActive && billParameter.name?.startsWith("prelevements_sociaux.reductions_cotisations_sociales.allegement_general.ensemble_des_entreprises.plafond")} <div class="flex bg-gray-100 px-3"> <div class="mb-1 rounded-sm bg-red-100 p-1 text-sm"> <span class="font-bold text-le-rouge-bill" diff --git a/src/lib/components/variables/VariableValueChange.svelte b/src/lib/components/variables/VariableValueChange.svelte index e383c1798e7f361ad77d08b02269a43ce9509298..312e180d9dd2c1c768828c1c9e91a096ab2709a4 100644 --- a/src/lib/components/variables/VariableValueChange.svelte +++ b/src/lib/components/variables/VariableValueChange.svelte @@ -1,11 +1,7 @@ <script lang="ts"> - import { getContext } from "svelte" - import type { Writable } from "svelte/store" - import ValueChange from "$lib/components/ValueChange.svelte" import type { EvaluationByName } from "$lib/decompositions" - import publicConfig from "$lib/public_config" - import type { ParametricReform } from "$lib/reforms" + import { billName, date, revaluationName, shared } from "$lib/shared.svelte" import { getUnitAtDate } from "$lib/units" import { variableSummaryByName, @@ -24,7 +20,6 @@ bold?: boolean } - const { reformName, revaluationName } = publicConfig let { compact = false, evaluationByName, @@ -35,17 +30,12 @@ bold = false, }: Props = $props() - const date = getContext("date") as Writable<string> - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - let evaluation = $derived(evaluationByName[name]) let variableSummary = $derived( - reformName === undefined + billName === undefined ? variableSummaryByName[name] - : variableSummaryByNameByReformName[reformName][name], + : variableSummaryByNameByReformName[billName][name], ) </script> @@ -54,17 +44,17 @@ {compact} {inline} {legend} - unitName={getUnitAtDate(variableSummary.unit, $date)?.name} + unitName={getUnitAtDate(variableSummary.unit, date)?.name} valueByCalculationName={valueByCalculationName ?? { amendment: - Object.keys($parametricReform).length === 0 + Object.keys(shared.parametricReform).length === 0 ? undefined : Math.abs( evaluation?.calculationEvaluationByName["amendment"] ?.deltaAtVectorIndex ?? 0, ), bill: - reformName === undefined + billName === undefined ? undefined : Math.abs( evaluation?.calculationEvaluationByName["bill"] diff --git a/src/lib/components/variables/VariableView.svelte b/src/lib/components/variables/VariableView.svelte index 6f5ab636b64158fa3b9b00cbe873bd7abed31b1d..7df0ec2c48a0ae1a0bd5ac147dc7bab3254658ef 100644 --- a/src/lib/components/variables/VariableView.svelte +++ b/src/lib/components/variables/VariableView.svelte @@ -2,18 +2,17 @@ import type { Variable } from "@openfisca/json-model" import { getContext } from "svelte" - import { page } from "$app/stores" import FormulaView from "$lib/components/variables/FormulaView.svelte" import VariableInput from "$lib/components/variables/VariableInput.svelte" import { decompositionCoreByName } from "$lib/decompositions" import publicConfig from "$lib/public_config" + import { date, year } from "$lib/shared.svelte" import type { Situation } from "$lib/situations" import type { SelfTargetAProps } from "$lib/urls" import type { ValuesByCalculationNameByVariableName } from "$lib/variables" import { buildInstantFormulaAndReferencesArray } from "$lib/variables" interface Props { - date: string editable: boolean inputInstantsByVariableName: { [name: string]: Set<string> @@ -22,19 +21,17 @@ situationIndex: number valuesByCalculationNameByVariableName: ValuesByCalculationNameByVariableName variable: Variable - year: number } const { openfiscaRepository } = publicConfig + let { - date, editable, inputInstantsByVariableName = $bindable(), situation = $bindable(), situationIndex, valuesByCalculationNameByVariableName = $bindable(), variable, - year, }: Props = $props() const dateFormatter = new Intl.DateTimeFormat("fr-FR", { dateStyle: "full" }) diff --git a/src/lib/shared.svelte.ts b/src/lib/shared.svelte.ts new file mode 100644 index 0000000000000000000000000000000000000000..4eac4e2266117ee8284e726e718857ba7417a22d --- /dev/null +++ b/src/lib/shared.svelte.ts @@ -0,0 +1,1065 @@ +import "intro.js/introjs.css" + +import { + getRolePersonsIdKey, + type GroupEntity, + type PopulationWithoutId, + type Waterfall, +} from "@openfisca/json-model" +import "iconify-icon" +import { v4 as uuidV4 } from "uuid" + +import type { BudgetSimulation } from "$lib/budgets" +import { + requestAllTestCasesCalculations, + type CalculationName, + type RequestedSituationIndexByCalculationName, +} from "$lib/calculations.svelte" +import { + buildDecompositionByNameFromCore, + decompositionCoreByName, + decompositionCoreByNameByReformName, + nonVirtualVariablesName, + nonVirtualVariablesNameByReformName, + updateEvaluations, + updateEvaluationsVectorIndex, + waterfalls, + type EvaluationByName, + type DecompositionByName, +} from "$lib/decompositions" +import type { DisplayMode } from "$lib/displays" +import { entityByKey } from "$lib/entities" +import { trackTestCaseShowAllVariables } from "$lib/matomo" +import { metadata } from "$lib/metadata" +import { getNavbarConfig, type NavbarConfig } from "$lib/navbar" +import publicConfig from "$lib/public_config" +import { type ParametricReform, reformMetadataByName } from "$lib/reforms" +import { + extractInputInstantsFromTestCases, + getPopulationReservedKeys, + indexOfSituationPopulationId, + testCasesCore, + type Axis, + type Calculation, + type CalculationByName, + type Situation, + type SituationWithAxes, + type TestCasesCalculationInput, +} from "$lib/situations" +import { + budgetEditableParametersName, + budgetVariableNameByVariableName, + budgetVariablesConfig, + budgetVariablesName, + otherCalculatedVariablesName, + summaryCalculatedVariablesName, + type BudgetVariable, + type ValuesByCalculationNameByVariableName, + type VariableValue, + type VariableValues, + variableSummaryByName, + variableSummaryByNameByReformName, +} from "$lib/variables" + +export interface Shared { + axes: Axis[][] + displayMode?: DisplayMode + budgetSimulation?: BudgetSimulation + calculationByName: CalculationByName + decompositionByName: DecompositionByName + evaluationByNameArray: EvaluationByName[] + inputInstantsByVariableNameArray: Array<{ + [name: string]: Set<string> + }> + navbarConfig: NavbarConfig + parametricReform: ParametricReform + requestedSimulationEmail?: string // Budget simulation requests + requestedSimulationSent: boolean // Budget simulation requests + requestedVariablesNameToCalculate?: Set<string> // Budget simulation requests + searchActive: boolean // Search + searchVariableName?: string // Search + showNulls: boolean + showTutorial: boolean + testCases: Situation[] + testCasesIndex: number[] + valuesByCalculationNameByVariableNameArray: ValuesByCalculationNameByVariableName[] + vectorLength: number + waterfall: Waterfall +} + +const { reformName: reformBillName, revaluationName: reformRevaluationName } = + publicConfig + +let axisBySituationIndex: { [situationIndex: string]: Axis } = {} + +let budgetSimulationAbortController = new AbortController() + +/** + * Bill (PLF) and revaluation (contrefactuel) + */ + +export const billName: string | undefined = + reformBillName === undefined + ? undefined + : reformMetadataByName[reformBillName] === undefined + ? undefined + : reformBillName +export const billActive: boolean = billName !== undefined + +export const revaluationName: string | undefined = + reformRevaluationName === undefined + ? undefined + : reformMetadataByName[reformRevaluationName] === undefined + ? undefined + : reformRevaluationName + +const today = new Date() +export const year = + today.getFullYear() + (today.getMonth() > 1 /* => After February */ ? 1 : 0) +export const yearPLF = + today.getFullYear() + + (billActive && today.getMonth() > 1 /* => After February */ ? 1 : 0) +export const date = `${year}-01-01` +export const budgetDate = `${yearPLF}-01-01` + +export const shared: Shared = $state({ + axes: [], + calculationByName: {}, + // Note: We always use the reform decomposition, because it is more + // complete than decomposition before reform. + decompositionByName: buildDecompositionByNameFromCore( + billName === undefined + ? decompositionCoreByName + : (decompositionCoreByNameByReformName[billName] ?? + decompositionCoreByName), + ), + evaluationByNameArray: new Array(testCasesCore.length).fill( + {}, + ) as EvaluationByName[], + inputInstantsByVariableNameArray: + extractInputInstantsFromTestCases(testCasesCore), + navbarConfig: getNavbarConfig("/"), + parametricReform: {}, + requestedSimulationSent: false, + searchActive: false, + showNulls: false, + showTutorial: false, + testCases: structuredClone(testCasesCore), + testCasesIndex: [], + valuesByCalculationNameByVariableNameArray: new Array( + testCasesCore.length, + ).fill({}), + vectorLength: 1, + waterfall: waterfalls[0], +}) + +// TODO svelte5: remove this and import from calculations +const calculationNames: CalculationName[] = [ + "law", + "revaluation", + "bill", + "amendment", +] +const testCasesAbortControllers: { + [calculationName: string]: AbortController +} = Object.fromEntries( + calculationNames.map((calculationName) => [ + calculationName, + new AbortController(), + ]), +) + +const vectorIndexes = new Array(testCasesCore.length).fill(0) + +if (shared.showNulls) { + trackTestCaseShowAllVariables() +} + +function buildBudgetDemandBody( + variableName: string, + outputVariables: string[], + quantileBaseVariable: string[], + quantileCompareVariables: string[], + includeDisplay = true, +) { + const budgetParametricReform = Object.fromEntries( + Object.entries(shared.parametricReform).filter(([parameterName]) => + budgetEditableParametersName.has(parameterName), + ), + ) + return { + amendement: budgetParametricReform, + base: yearPLF, + displayMode: includeDisplay ? shared.displayMode : undefined, + metadata, + output_variables: outputVariables, + quantile_base_variable: quantileBaseVariable, + quantile_compare_variables: quantileCompareVariables, + winners_loosers_variable: variableName, + quantile_nb: 10, + plf: billName === undefined ? undefined : yearPLF, + } +} + +export async function calculateBudget( + budgetVariableName: string, + // budgetCalculationNames: Set<CalculationName>, +): Promise<void> { + budgetSimulationAbortController.abort() + budgetSimulationAbortController = new AbortController() + if (!budgetVariablesName.has(budgetVariableName)) { + console.error( + `Budget calculation for variable ${budgetVariableName} is not available`, + ) + shared.budgetSimulation = undefined + return + } + const variableName = budgetVariableNameByVariableName[budgetVariableName] + const variableConfig: BudgetVariable = budgetVariablesConfig[variableName] + const body = JSON.stringify( + buildBudgetDemandBody( + variableName, + variableConfig.outputVariables, + variableConfig.quantileBaseVariable, + variableConfig.quantileCompareVariables, + ), + ) + shared.budgetSimulation = undefined + const response = await fetch("/budgets", { + body, + headers: { + Accept: "application/json", + "Content-Type": "application/json; charset=utf-8", + }, + method: "POST", + signal: budgetSimulationAbortController.signal, + }) + if (!response.ok) { + console.error( + `${$page.url}: Error while calculating budget:\n${body}\n${response.status} ${response.statusText}`, + ) + console.error(await response.text()) + shared.budgetSimulation = { + errors: [ + `Une erreur inattendue (${response.status} ${response.statusText}) s'est produite et les impacts budgétaires ne sont pas disponibles pour le moment. Écrivez-nous à leximpact@assemblee-nationale.fr.`, + ], + isPublic: true, + } + shared.budgetSimulation = undefined + return + } + shared.budgetSimulation = await response.json() +} + +export async function calculateTestCases( + situationIndexByCalculationName: RequestedSituationIndexByCalculationName, +) { + $inspect("3") + const aggregatedSituation: SituationWithAxes = {} + const axesData: { situationIndex: number; variables: string[] } = + shared.axes.reduce( + ( + result: { situationIndex?: number; variables: string[] }, + parallelAxes, + ) => { + for (const axis of parallelAxes) { + result.situationIndex = axis.situationIndex + if (result.variables.includes(axis.name)) { + continue + } + result.variables.push(axis.name) + } + return result + }, + { variables: [] }, + ) + const entities = Object.values(entityByKey) + if ( + Object.values(situationIndexByCalculationName).some( + (situationIndex) => situationIndex === null, + ) + ) { + $inspect("4") + let personIndex = -1 + // Some of the calculations are requested to be done for every situations. + // Aggregate every situations into a single one without calculated variables. + for (const [situationIndex, situation] of shared.testCases.entries()) { + const inputInstantsByVariableName = + shared.inputInstantsByVariableNameArray[situationIndex] + const situationPrefix = `Cas type n°${situationIndex + 1} | ` + for (const entity of entities) { + let entitySituation = situation[entity.key_plural as string] + if (entitySituation === undefined) { + continue + } + let aggregatedEntitySituation = + aggregatedSituation[entity.key_plural as string] + if (aggregatedEntitySituation === undefined) { + aggregatedEntitySituation = aggregatedSituation[ + entity.key_plural as string + ] = {} + } + const reservedKeys = getPopulationReservedKeys(entity) + for (const [populationId, population] of Object.entries( + entitySituation, + ).sort(([populationId1], [populationId2]) => + populationId1.localeCompare(populationId2), + )) { + const transformedPopulation: PopulationWithoutId = {} + if (!entity.is_person) { + for (const role of (entity as GroupEntity).roles) { + const personsIdKey = getRolePersonsIdKey(role) + const personsId = population[personsIdKey] as string[] | undefined + if (personsId === undefined) { + continue + } + transformedPopulation[personsIdKey] = personsId.map( + (personId) => situationPrefix + personId, + ) + } + } else { + personIndex++ + } + for (const [variableName, variableValueByInstant] of Object.entries( + population, + )) { + if ( + reservedKeys.has(variableName) || + (entity.is_person && + axesData.situationIndex === personIndex && + axesData.variables.includes(variableName)) + ) { + continue + } + const inputVariableValueByInstant: { + [instant: string]: VariableValue | null + } = {} + const inputInstants = + inputInstantsByVariableName[variableName] ?? new Set<string>() + for (const [instant, variableValue] of Object.entries( + variableValueByInstant as { + [instant: string]: VariableValue | null + }, + )) { + if (!inputInstants.has(instant)) { + // Ignore calculated value. + continue + } + inputVariableValueByInstant[instant] = variableValue + } + if (Object.keys(inputVariableValueByInstant).length > 0) { + transformedPopulation[variableName] = inputVariableValueByInstant + } + } + aggregatedEntitySituation[situationPrefix + populationId] = + transformedPopulation + } + } + } + + if (shared.axes.length > 0) { + aggregatedSituation.axes = shared.axes + } + } + $inspect("5") + + const message = { + period: year.toString(), + } + const newCalculationByName: CalculationByName = {} + + const lawSituationIndex = situationIndexByCalculationName.law + if (lawSituationIndex !== undefined) { + $inspect("6") + const calculation: Calculation = (newCalculationByName.law = { + running: true, + }) + let situation: SituationWithAxes + if (lawSituationIndex === null) { + situation = aggregatedSituation + } else { + calculation.situationIndex = lawSituationIndex + situation = cleanSituation( + shared.testCases[lawSituationIndex], + shared.axes, + ) + } + calculation.input = { + ...message, + situation, + variables: [ + ...summaryCalculatedVariablesName, + ...otherCalculatedVariablesName, + ...nonVirtualVariablesName, + ], + } + sendTestCasesSimulation("law", calculation.input) // Don't wait for result. + } + $inspect("7") + + const revaluationSituationIndex = situationIndexByCalculationName.revaluation + if ( + revaluationSituationIndex !== undefined && + revaluationName !== undefined + ) { + $inspect("8") + const calculation: Calculation = (newCalculationByName.revaluation = { + running: true, + }) + let situation: SituationWithAxes + if (revaluationSituationIndex === null) { + situation = aggregatedSituation + } else { + calculation.situationIndex = revaluationSituationIndex + situation = cleanSituation( + shared.testCases[revaluationSituationIndex], + shared.axes, + ) + } + calculation.input = { + ...message, + reform: revaluationName, + situation, + variables: [ + ...summaryCalculatedVariablesName, + ...otherCalculatedVariablesName, + ...(nonVirtualVariablesNameByReformName[revaluationName] ?? + nonVirtualVariablesName), + ], + } + sendTestCasesSimulation("revaluation", calculation.input) // Don't wait for result. + } + $inspect("9") + + const billSituationIndex = situationIndexByCalculationName.bill + if (billSituationIndex !== undefined && billActive) { + $inspect("10") + const calculation: Calculation = (newCalculationByName.bill = { + running: true, + }) + let situation: SituationWithAxes + if (billSituationIndex === null) { + situation = aggregatedSituation + } else { + calculation.situationIndex = billSituationIndex + situation = cleanSituation( + shared.testCases[billSituationIndex], + shared.axes, + ) + } + calculation.input = { + ...message, + reform: billName, + situation, + variables: [ + ...summaryCalculatedVariablesName, + ...otherCalculatedVariablesName, + ...(nonVirtualVariablesNameByReformName[billName] ?? + nonVirtualVariablesName), + ], + } + sendTestCasesSimulation("bill", calculation.input) // Don't wait for result. + } + $inspect("11") + + const amendmentSituationIndex = situationIndexByCalculationName.amendment + if (amendmentSituationIndex !== undefined) { + $inspect("12") + if (Object.keys(shared.parametricReform).length === 0) { + // Remove amendment evaluations from decompositions. + shared.evaluationByNameArray = shared.evaluationByNameArray.map( + (evaluationByName, situationIndex): EvaluationByName => { + const updatedEvaluationByName = Object.fromEntries( + Object.entries(evaluationByName).map( + ([variableName, evaluation]) => { + const calculationEvaluationByName = { + ...evaluation.calculationEvaluationByName, + } + delete calculationEvaluationByName["amendment"] + return [ + variableName, + { ...evaluation, calculationEvaluationByName }, + ] + }, + ), + ) + return updateEvaluations( + shared.decompositionByName, + updatedEvaluationByName, + shared.testCases[situationIndex].slider?.vectorIndex ?? 0, + shared.vectorLength, + waterfalls, + ) + }, + ) + + // Remove amendment values. + shared.valuesByCalculationNameByVariableNameArray = + shared.valuesByCalculationNameByVariableNameArray.map( + (valuesByCalculationNameByVariableName) => + Object.fromEntries( + Object.entries(valuesByCalculationNameByVariableName).map( + ([variableName, valuesByCalculationName]) => { + const updatedValuesByCalculationName = { + ...valuesByCalculationName, + } + delete updatedValuesByCalculationName["amendment"] + return [variableName, updatedValuesByCalculationName] + }, + ), + ), + ) + } else { + const calculation: Calculation = (newCalculationByName.amendment = { + running: true, + }) + let situation: SituationWithAxes + if (amendmentSituationIndex === null) { + situation = aggregatedSituation + } else { + calculation.situationIndex = amendmentSituationIndex + situation = cleanSituation( + shared.testCases[amendmentSituationIndex], + shared.axes, + ) + } + calculation.input = { + ...message, + parametric_reform: shared.parametricReform, + reform: billName ?? revaluationName, // Will be removed when billName and revaluationName are undefined. + situation, + variables: [ + ...summaryCalculatedVariablesName, + ...otherCalculatedVariablesName, + ...(nonVirtualVariablesNameByReformName[billName as string] ?? + nonVirtualVariablesNameByReformName[revaluationName as string] ?? + nonVirtualVariablesName), + ], + } + sendTestCasesSimulation("amendment", calculation.input) // Don't wait for result. + } + } + $inspect("13") + + shared.calculationByName = newCalculationByName + $inspect("14") +} + +export function calculateTestCasesAdditionalVariables( + additionalVariablesName: Set<string>, +): void { + const newCalculationByName = { ...shared.calculationByName } + for (const [calculationName, calculation] of Object.entries( + newCalculationByName, + )) { + if (calculation.input === undefined) { + continue + } + + let variablesAdded = false + const variablesName = [...calculation.input.variables] + for (const variableName of additionalVariablesName) { + if (!variablesName.includes(variableName)) { + variablesName.push(variableName) + variablesAdded = true + } + } + + if (variablesAdded) { + const input = { ...calculation.input, variables: variablesName } + newCalculationByName[calculationName as CalculationName] = { + ...calculation, + input, + running: true, + } + sendTestCasesSimulation(calculationName as CalculationName, input) // Don't wait for result. + } + } + shared.calculationByName = newCalculationByName +} + +function cleanSituation( + situationToClean: Situation, + axes: Axis[][], +): SituationWithAxes { + let situation: SituationWithAxes = {} + for (const entity of Object.values(entityByKey)) { + let entitySituation = situationToClean[entity.key_plural as string] + if (entitySituation === undefined) { + continue + } + situation[entity.key_plural as string] = entitySituation + } + if (axes.length > 0) { + situation.axes = axes.map((parallelAxes) => + parallelAxes.map((axis) => ({ + ...axis, + index: (axis.index as number) - (axis.situationIndex as number), + situationIndex: 0, + })), + ) + } + return situation +} + +export async function sendBudgetSimulationDemand() { + const budgetVariableName = shared.displayMode?.parametersVariableName + if ( + budgetVariableName === undefined || + !budgetVariablesName.has(budgetVariableName) + ) { + console.error( + `Budget calculation for variable ${budgetVariableName} is not available`, + ) + shared.budgetSimulation = undefined + return + } + const variableName = budgetVariableNameByVariableName[budgetVariableName] + const variableConfig: BudgetVariable = budgetVariablesConfig[variableName] + const simulationBody = buildBudgetDemandBody( + variableName, + variableConfig.outputVariables, + variableConfig.quantileBaseVariable, + variableConfig.quantileCompareVariables, + false, + ) + const urlString = "/budgets/demands" + const res = await fetch(urlString, { + body: JSON.stringify( + { + displayMode: shared.displayMode, + email: shared.requestedSimulationEmail, + request: Object.fromEntries( + Object.entries(shared.parametricReform).filter(([parameterName]) => + budgetEditableParametersName.has(parameterName), + ), + ), + simulation: simulationBody, + }, + null, + 2, + ), + headers: { + Accept: "application/json", + "Content-Type": "application/json; charset=utf-8", + }, + method: "POST", + }) + if (!res.ok) { + shared.requestedSimulationEmail = undefined + console.error( + `Error ${ + res.status + } while sending a simulation request at ${urlString}\n\n${await res.text()}`, + ) + return + } + shared.requestedSimulationEmail = undefined +} + +async function sendTestCasesSimulation( + calculationName: CalculationName, + { + period, + parametric_reform, + reform, + situation, + variables, + }: TestCasesCalculationInput, +) { + const completeVariableSummaryByName = + billName === undefined + ? variableSummaryByName + : variableSummaryByNameByReformName[billName] + try { + // Note: crypto.randomUUID() is not supported by Safari before version 15.4. + // const token = crypto.randomUUID() + const token = uuidV4() + testCasesAbortControllers[calculationName].abort() + testCasesAbortControllers[calculationName] = new AbortController() + const response = await fetch("/test_cases", { + body: JSON.stringify({ + period, + parametric_reform, + reform, + situation, + title: calculationName, + token, + variables, + }), + headers: { + Accept: "application/json", + "Content-Type": "application/json; charset=utf-8", + }, + method: "POST", + signal: testCasesAbortControllers[calculationName].signal, + }) + if (!response.ok) { + console.error( + `Error while submitting test case simulation:\n${response.status} ${response.statusText}`, + ) + console.error(await response.text()) + return + } + const { evaluations } = (await response.json()) as { + evaluations: [ + { + entity: string + name: string + value: VariableValues + }, + ] + token: string + } + + let calculation = shared.calculationByName[calculationName] as Calculation + + let updatedEvaluationByNameArray = + calculation.situationIndex === undefined + ? shared.evaluationByNameArray + : [...shared.evaluationByNameArray] + for (const { + entity: entityKey, + name: variableName, + value, + } of evaluations) { + // Round returned values if unit of variable is a currency. + const variable = completeVariableSummaryByName[variableName] + const roundedValue = + variable.unit === "currency" || variable.unit?.startsWith("currency-") + ? value.map((valueAtIndex) => Math.round(valueAtIndex as number)) + : value + + const entity = entityByKey[entityKey] + + if (calculation.situationIndex === undefined) { + // Variable has been computed for all test cases. + + // Count total population of test cases. + let testCasesPopulationCount = 0 + for (const situation of shared.testCases) { + const entitySituation = situation[entity.key_plural as string] ?? {} + const situationPopulationCount = Object.keys(entitySituation).length + testCasesPopulationCount += situationPopulationCount + } + + // Split evaluation.value vector for each situation. + { + let testCasesPopulationIndex = 0 + shared.valuesByCalculationNameByVariableNameArray = + shared.valuesByCalculationNameByVariableNameArray.map( + ( + valuesByCalculationNameByVariableName, + situationIndex, + ): ValuesByCalculationNameByVariableName => { + const situation = shared.testCases[situationIndex] + const entitySituation = + situation[entity.key_plural as string] ?? {} + let values = [] + for ( + let index = testCasesPopulationIndex, vectorIndex = 0; + vectorIndex < shared.vectorLength; + index += testCasesPopulationCount, vectorIndex++ + ) { + for (const situationPersonIndex of Object.keys( + entitySituation, + ).keys()) { + values.push(roundedValue[index + situationPersonIndex]) + } + } + testCasesPopulationIndex += Object.keys(entitySituation).length + const valuesByCalculationName = + valuesByCalculationNameByVariableName[variableName] ?? {} + return { + ...valuesByCalculationNameByVariableName, + [variableName]: { + ...valuesByCalculationName, + [calculationName]: values, + }, + } + }, + ) + } + } else { + // Variable has been computed for a single test case. + + const updatedValuesByCalculationNameByVariableNameArray = [ + ...shared.valuesByCalculationNameByVariableNameArray, + ] + const valuesByCalculationNameByVariableName = { + ...updatedValuesByCalculationNameByVariableNameArray[ + calculation.situationIndex + ], + } + const valuesByCalculationName = + valuesByCalculationNameByVariableName[variableName] ?? {} + valuesByCalculationNameByVariableName[variableName] = { + ...valuesByCalculationName, + [calculationName]: roundedValue, + } + updatedValuesByCalculationNameByVariableNameArray[ + calculation.situationIndex + ] = valuesByCalculationNameByVariableName + shared.valuesByCalculationNameByVariableNameArray = + updatedValuesByCalculationNameByVariableNameArray + } + + // Update evaluations for decompositions. + const calculationNonVirtualVariablesName = + calculationName === "law" + ? nonVirtualVariablesName + : (nonVirtualVariablesNameByReformName[billName as string] ?? + nonVirtualVariablesName) + if (calculationNonVirtualVariablesName.includes(variableName)) { + if (calculation.situationIndex === undefined) { + // Variable has been computed for all test cases. + + // First, update delta and values of evaluations. + updatedEvaluationByNameArray = updatedEvaluationByNameArray.map( + (evaluationByName, situationIndex): EvaluationByName => { + const situation = shared.testCases[situationIndex] + const values = shared.valuesByCalculationNameByVariableNameArray[ + situationIndex + ][variableName][calculationName] as VariableValues + const entitySituation = + situation[entity.key_plural as string] ?? {} + const situationPopulationCount = + Object.keys(entitySituation).length + let delta = new Array(shared.vectorLength).fill(0) + for (const situationPersonIndex of Object.keys( + entitySituation, + ).keys()) { + for ( + let index = situationPersonIndex, vectorIndex = 0; + vectorIndex < shared.vectorLength; + index += situationPopulationCount, vectorIndex++ + ) { + delta[vectorIndex] += values[index] + } + } + const evaluation = evaluationByName[variableName] + const calculationEvaluationByName = + evaluation?.calculationEvaluationByName ?? {} + return { + ...evaluationByName, + [variableName]: { + ...(evaluation ?? {}), + calculationEvaluationByName: { + ...calculationEvaluationByName, + [calculationName]: { + ...(calculationEvaluationByName[calculationName] ?? {}), + delta, + deltaAtVectorIndex: + delta[situation.slider?.vectorIndex ?? 0] ?? 0, + }, + }, + fromOpenFisca: true, + }, + } + }, + ) + } else { + // Variable has been computed for a single test case. + + // First, update delta and values of evaluations. + const situation = shared.testCases[calculation.situationIndex] + const values = shared.valuesByCalculationNameByVariableNameArray[ + calculation.situationIndex + ][variableName][calculationName] as VariableValues + const entitySituation = situation[entity.key_plural as string] ?? {} + const situationPopulationCount = Object.keys(entitySituation).length + let delta = new Array(shared.vectorLength).fill(0) + for (const situationPersonIndex of Object.keys( + entitySituation, + ).keys()) { + for ( + let index = situationPersonIndex, vectorIndex = 0; + vectorIndex < shared.vectorLength; + index += situationPopulationCount, vectorIndex++ + ) { + delta[vectorIndex] += values[index] + } + } + const evaluationByName = { + ...updatedEvaluationByNameArray[calculation.situationIndex], + } + const evaluation = evaluationByName[variableName] + const calculationEvaluationByName = + evaluation?.calculationEvaluationByName ?? {} + evaluationByName[variableName] = { + ...(evaluation ?? {}), + calculationEvaluationByName: { + ...calculationEvaluationByName, + [calculationName]: { + ...(calculationEvaluationByName[calculationName] ?? {}), + delta, + deltaAtVectorIndex: + delta[situation.slider?.vectorIndex ?? 0] ?? 0, + }, + }, + fromOpenFisca: true, + } + } + } + } + + // Update deltaSums of evaluations from their new delta. + if (calculation.situationIndex === undefined) { + shared.evaluationByNameArray = updatedEvaluationByNameArray.map( + (evaluationByName, situationIndex) => + updateEvaluations( + shared.decompositionByName, + evaluationByName, + shared.testCases[situationIndex].slider?.vectorIndex ?? 0, + shared.vectorLength, + waterfalls, + ), + ) + } else { + updatedEvaluationByNameArray[calculation.situationIndex] = + updateEvaluations( + shared.decompositionByName, + updatedEvaluationByNameArray[calculation.situationIndex], + shared.testCases[calculation.situationIndex].slider?.vectorIndex ?? 0, + shared.vectorLength, + waterfalls, + ) + shared.evaluationByNameArray = updatedEvaluationByNameArray + } + + calculation = { ...calculation } + delete calculation.running + shared.calculationByName[calculationName] = calculation + } catch (e) { + if (e?.name === "AbortError") { + return + } + } +} + +export function updateOnSliderChange(situations: Situation[]): void { + let changedAxesSituationIndex: number[] = [] + let evaluationByNameArrayChanged = false + const updatedEvaluationByNameArray = [...shared.evaluationByNameArray] + for (const [situationIndex, situation] of situations.entries()) { + const slider = situation.slider + if (slider === undefined) { + if (axisBySituationIndex[situationIndex] !== undefined) { + axisBySituationIndex = { ...axisBySituationIndex } + delete axisBySituationIndex[situationIndex] + changedAxesSituationIndex.push(situationIndex) + + // Change only vectorIndexes content, because vectorIndexes reactivity + // is not needed. + vectorIndexes[situationIndex] = 0 + } + } else { + const currentAxis = axisBySituationIndex[situationIndex] + if ( + currentAxis === undefined || + currentAxis.name !== slider.name || + currentAxis.min !== slider.min || + currentAxis.max !== slider.max + ) { + const entity = entityByKey[slider.entity] + + let previousSituationsPopulationCount = 0 + for (const situation of shared.testCases.slice(0, situationIndex)) { + const entitySituation = situation[entity.key_plural as string] ?? {} + const populationCount = Object.keys(entitySituation).length + previousSituationsPopulationCount += populationCount + } + const axis = { + count: 101, + index: + previousSituationsPopulationCount + + indexOfSituationPopulationId(entity, situation, slider.id), + max: slider.max, + min: slider.min, + name: slider.name, + period: year.toString(), // Previous years are added later. + situationIndex: previousSituationsPopulationCount, + } + axisBySituationIndex = { + ...axisBySituationIndex, + [situationIndex]: axis, + } + changedAxesSituationIndex.push(situationIndex) + } + + const vectorIndex = slider.vectorIndex + if (vectorIndex !== vectorIndexes[situationIndex]) { + // Change only vectorIndexes content, because vectorIndexes reactivity + // is not needed. + vectorIndexes[situationIndex] = vectorIndex + + // Update evaluations. + let evaluationByName = updatedEvaluationByNameArray[situationIndex] + const updatedEvaluationByName = updateEvaluationsVectorIndex( + evaluationByName, + vectorIndex, + ) + if (updatedEvaluationByName !== evaluationByName) { + updatedEvaluationByNameArray[situationIndex] = updatedEvaluationByName + evaluationByNameArrayChanged = true + } + } + } + } + + if (changedAxesSituationIndex.length > 0) { + const parallelAxes = Object.entries(axisBySituationIndex) + .sort(([situationIndex1], [situationIndex2]) => + situationIndex1.localeCompare(situationIndex2), + ) + .map(([, axis]) => axis) + .reduce((parallelAxes, axis) => { + // Add previous years of each axis. + const year = parseInt(axis.period as string) + parallelAxes.push( + { + ...axis, + period: (year - 2).toString(), + }, + { + ...axis, + period: (year - 1).toString(), + }, + axis, + ) + return parallelAxes + }, [] as Axis[]) + + shared.axes = parallelAxes.length === 0 ? [] : [parallelAxes] + + let updatedVectorLength = 1 + for (const parallelAxes of shared.axes) { + // All the parallel axes have the same count. + const axis = parallelAxes[0] + updatedVectorLength *= axis.count + } + shared.vectorLength = updatedVectorLength + + for (const situationIndex of changedAxesSituationIndex) { + updatedEvaluationByNameArray[situationIndex] = updateEvaluations( + shared.decompositionByName, + updatedEvaluationByNameArray[situationIndex], + vectorIndexes[situationIndex], + shared.vectorLength, + waterfalls, + ) + evaluationByNameArrayChanged = true + } + + // Launch calculations if the axes have not been deleted (ie shared.vectorLength > 1). + const situationIndex = + changedAxesSituationIndex.length === 1 + ? changedAxesSituationIndex[0] + : undefined + requestAllTestCasesCalculations(situationIndex ?? null) + } + + if (evaluationByNameArrayChanged) { + shared.evaluationByNameArray = updatedEvaluationByNameArray + } +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 8bb9855a2447b82ed18d2378c6ed719cf379d4da..eb18b2ba3a429d2404ef480fc63ccb8e42a6321f 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,419 +1,73 @@ <script lang="ts"> - import { run } from "svelte/legacy" - import "intro.js/introjs.css" import "../app.css" - import { - getRolePersonsIdKey, - type GroupEntity, - type PopulationWithoutId, - } from "@openfisca/json-model" import "iconify-icon" - import { setContext, untrack } from "svelte" - import { writable, type Writable } from "svelte/store" - import { v4 as uuidV4 } from "uuid" + import { onMount, setContext } from "svelte" + import { writable } from "svelte/store" import type { LayoutData } from "./$types" - import { browser } from "$app/environment" import { onNavigate } from "$app/navigation" import { page } from "$app/stores" - import type { BudgetSimulation } from "$lib/budgets" import { requestAllTestCasesCalculations, - type CalculationName, - type RequestedCalculations, - type RequestedSituationIndexByCalculationName, - calculationNames, requestedCalculations, } from "$lib/calculations.svelte" import NavBar from "$lib/components/NavBar.svelte" - import { - buildDecompositionByNameFromCore, - decompositionCoreByName, - decompositionCoreByNameByReformName, - nonVirtualVariablesName, - nonVirtualVariablesNameByReformName, - updateEvaluations, - updateEvaluationsVectorIndex, - waterfalls, - type EvaluationByName, - type DecompositionByName, - } from "$lib/decompositions" - import type { DisplayMode } from "$lib/displays" - import { entityByKey } from "$lib/entities" - import { - trackPageView, - trackParametricReform, - trackTestCaseShowAllVariables, - } from "$lib/matomo" - import { metadata } from "$lib/metadata" - import { getNavbarConfig, type NavbarConfig } from "$lib/navbar" + import { trackPageView, trackParametricReform } from "$lib/matomo" + import { getNavbarConfig } from "$lib/navbar" import publicConfig from "$lib/public_config" - import { reformMetadataByName, type ParametricReform } from "$lib/reforms" import { - extractInputInstantsFromTestCases, - getPopulationReservedKeys, - indexOfSituationPopulationId, - testCasesCore, - type Axis, - type Calculation, - type CalculationByName, - type Situation, - type SituationWithAxes, - type TestCasesCalculationInput, - } from "$lib/situations" - import { - budgetEditableParametersName, - budgetVariableNameByVariableName, - budgetVariablesConfig, - budgetVariablesName, - otherCalculatedVariablesName, - summaryCalculatedVariablesName, - type BudgetVariable, - type ValuesByCalculationNameByVariableName, - type VariableValue, - type VariableValues, - variableSummaryByName, - variableSummaryByNameByReformName, - } from "$lib/variables" + calculateBudget, + calculateTestCases, + calculateTestCasesAdditionalVariables, + sendBudgetSimulationDemand, + shared, + updateOnSliderChange, + } from "$lib/shared.svelte" interface Props { data: LayoutData children?: import("svelte").Snippet } - const { - baseUrl, - matomo: matomoConfig, - reformName, - revaluationName, - } = publicConfig - let { data, children }: Props = $props() - - let axisBySituationIndex: { [situationIndex: string]: Axis } = {} - - const budgetSimulation: Writable<BudgetSimulation | undefined> = - writable(undefined) - setContext("budgetSimulation", budgetSimulation) - let budgetSimulationAbortController = new AbortController() - - const calculationByName: Writable<CalculationByName> = writable({}) - setContext("calculationByName", calculationByName) + const { baseUrl, matomo: matomoConfig } = publicConfig - /** - * Bill (PLF) - */ + let { children }: Props = $props() - let currentBillName: string | undefined = - reformName === undefined - ? undefined - : reformMetadataByName[reformName] === undefined - ? undefined - : reformName - const billActive: Writable<boolean> = writable(currentBillName !== undefined) - setContext("billActive", billActive) - - let currentRevaluationName: string | undefined = - revaluationName === undefined - ? undefined - : reformMetadataByName[revaluationName] === undefined - ? undefined - : revaluationName + let { url } = $derived($page) const customOpenGraphRoutes = [ /\/fonctionnement/, /\/test_cases\/simulations\/.*/, ] - const today = new Date() - const year = writable( - today.getFullYear() + - (today.getMonth() > 1 /* => After February */ ? 1 : 0), - ) - setContext("year", year) - const yearPLF = writable( - today.getFullYear() + - ($billActive && today.getMonth() > 1 /* => After February */ ? 1 : 0), - ) - setContext("yearPLF", yearPLF) - const date = writable(`${$year}-01-01`) - setContext("date", date) - const budgetDate = writable(`${$yearPLF}-01-01`) - setContext("budgetDate", budgetDate) - - const decompositionByName = writable( - // Note: We always use the reform decomposition, because it is more - // complete than decomposition before reform. - buildDecompositionByNameFromCore( - currentBillName === undefined - ? decompositionCoreByName - : (decompositionCoreByNameByReformName[currentBillName] ?? - decompositionCoreByName), - ), - ) - setContext("decompositionByName", decompositionByName) - - const displayMode: Writable<DisplayMode | undefined> = writable(undefined) - setContext("displayMode", displayMode) - - const evaluationByNameArray = writable( - new Array(testCasesCore.length).fill({}) as EvaluationByName[], - ) - setContext("evaluationByNameArray", evaluationByNameArray) - - const inputInstantsByVariableNameArray: Writable< - Array<{ - [name: string]: Set<string> - }> - > = writable(extractInputInstantsFromTestCases(testCasesCore)) - setContext( - "inputInstantsByVariableNameArray", - inputInstantsByVariableNameArray, - ) - - const navbarConfig: Writable<NavbarConfig> = writable( - getNavbarConfig($page.route.id ?? "/"), - ) - setContext("navbarConfig", navbarConfig) - - const parametricReformValue: ParametricReform = {} - // let parametricReformValue: ParametricReform - // if (browser) { - // const parametricReformJson = localStorage.getItem("parametricReform") - // if (parametricReformJson !== null) { - // try { - // parametricReformValue = JSON.parse(parametricReformJson) - // } catch { - // parametricReformValue = {} - // } - // } else { - // parametricReformValue = {} - // } - // } else { - // parametricReformValue = {} - // } - const parametricReform = writable(parametricReformValue) - setContext("parametricReform", parametricReform) - - const requestedSimulationEmail: Writable<string | undefined> = - writable(undefined) - setContext("requestedSimulationEmail", requestedSimulationEmail) - - const requestedSimulationSent: Writable<boolean> = writable(false) - setContext("requestedSimulationSent", requestedSimulationSent) - - const requestedVariablesNameToCalculate: Writable<Set<string> | undefined> = - writable(undefined) - setContext( - "requestedVariablesNameToCalculate", - requestedVariablesNameToCalculate, - ) - - /* - * Search - * */ - const searchActive: Writable<boolean> = writable(false) - setContext("searchActive", searchActive) - - const searchVariableName: Writable<string | undefined> = writable(undefined) - setContext("searchVariableName", searchVariableName) - - const showNulls = writable(false) - setContext("showNulls", showNulls) - - const showTutorial = writable(publicConfig.showTutorial) - setContext("showTutorial", showTutorial) - - const testCasesValue: Situation[] = structuredClone(testCasesCore) - // let testCasesValue: Situation[] - // if (browser) { - // const testCasesJson = localStorage.getItem("testCases") - // if (testCasesJson !== null) { - // try { - // testCasesValue = JSON.parse(testCasesJson) - // } catch { - // testCasesValue = testCasesCore - // } - // } else { - // testCasesValue = testCasesCore - // } - // } else { - // testCasesValue = testCasesCore - // } - const testCases = writable(testCasesValue) - setContext("testCases", testCases) - - const testCasesIndex: Writable<number[]> = writable([]) - setContext("testCasesIndex", testCasesIndex) - - const testCasesAbortControllers: { - [calculationName: string]: AbortController - } = Object.fromEntries( - calculationNames.map((calculationName) => [ - calculationName, - new AbortController(), - ]), - ) - - const valuesByCalculationNameByVariableNameArray: Writable< - ValuesByCalculationNameByVariableName[] - > = writable(new Array(testCasesCore.length).fill({})) - setContext( - "valuesByCalculationNameByVariableNameArray", - valuesByCalculationNameByVariableNameArray, - ) - - const vectorLength = writable(1) - setContext("vectorLength", vectorLength) - - let vectorIndexes = new Array(testCasesCore.length).fill(0) - - let waterfall = writable(waterfalls[0]) - setContext("waterfall", waterfall) - - if (browser && matomoConfig !== undefined) { - matomo(matomoConfig) - } - - if (browser && $showNulls === true) { - trackTestCaseShowAllVariables() - } - - function buildBudgetDemandBody( - variableName: string, - outputVariables: string[], - quantileBaseVariable: string[], - quantileCompareVariables: string[], - includeDisplay = true, - ) { - const budgetParametricReform = Object.fromEntries( - Object.entries($parametricReform).filter(([parameterName]) => - budgetEditableParametersName.has(parameterName), - ), - ) - return { - amendement: budgetParametricReform, - base: $yearPLF, - displayMode: includeDisplay ? $displayMode : undefined, - metadata, - output_variables: outputVariables, - quantile_base_variable: quantileBaseVariable, - quantile_compare_variables: quantileCompareVariables, - winners_loosers_variable: variableName, - quantile_nb: 10, - plf: reformName === undefined ? undefined : $yearPLF, - } + if ($page.route.id) { + shared.navbarConfig = getNavbarConfig($page.route.id) } - async function calculateBudget( - budgetVariableName: string, - // budgetCalculationNames: Set<CalculationName>, - ): Promise<void> { - budgetSimulationAbortController.abort() - budgetSimulationAbortController = new AbortController() - if (!budgetVariablesName.has(budgetVariableName)) { - console.error( - `Budget calculation for variable ${budgetVariableName} is not available`, - ) - $budgetSimulation = undefined - return - } - const variableName = budgetVariableNameByVariableName[budgetVariableName] - const variableConfig: BudgetVariable = budgetVariablesConfig[variableName] - const body = JSON.stringify( - buildBudgetDemandBody( - variableName, - variableConfig.outputVariables, - variableConfig.quantileBaseVariable, - variableConfig.quantileCompareVariables, - ), - ) - $budgetSimulation = undefined - const response = await fetch("/budgets", { - body, - headers: { - Accept: "application/json", - "Content-Type": "application/json; charset=utf-8", - }, - method: "POST", - signal: budgetSimulationAbortController.signal, - }) - if (!response.ok) { - console.error( - `${$page.url}: Error while calculating budget:\n${body}\n${response.status} ${response.statusText}`, - ) - console.error(await response.text()) - $budgetSimulation = { - errors: [ - `Une erreur inattendue (${response.status} ${response.statusText}) s'est produite et les impacts budgétaires ne sont pas disponibles pour le moment. Écrivez-nous à leximpact@assemblee-nationale.fr.`, - ], - isPublic: true, - } - $budgetSimulation = undefined - return + $effect(() => { + if (matomoConfig !== undefined) { + matomo(matomoConfig) } - $budgetSimulation = await response.json() - } - - function calculateTestCasesAdditionalVariables( - additionalVariablesName: Set<string>, - ): void { - const newCalculationByName = { ...$calculationByName } - for (const [calculationName, calculation] of Object.entries( - newCalculationByName, - )) { - if (calculation.input === undefined) { - continue - } - - let variablesAdded = false - const variablesName = [...calculation.input.variables] - for (const variableName of additionalVariablesName) { - if (!variablesName.includes(variableName)) { - variablesName.push(variableName) - variablesAdded = true - } - } + }) - if (variablesAdded) { - const input = { ...calculation.input, variables: variablesName } - newCalculationByName[calculationName as CalculationName] = { - ...calculation, - input, - running: true, - } - sendTestCasesSimulation(calculationName as CalculationName, input) // Don't wait for result. - } + $effect(() => { + if (matomoConfig !== undefined && $page) { + trackPageView(url, matomoConfig.prependDomain) } - $calculationByName = newCalculationByName - } + }) - function cleanSituation( - situationToClean: Situation, - axes: Axis[][], - ): SituationWithAxes { - let situation: SituationWithAxes = {} - for (const entity of Object.values(entityByKey)) { - let entitySituation = situationToClean[entity.key_plural as string] - if (entitySituation === undefined) { - continue - } - situation[entity.key_plural as string] = entitySituation - } - if (axes.length > 0) { - situation.axes = axes.map((parallelAxes) => - parallelAxes.map((axis) => ({ - ...axis, - index: (axis.index as number) - (axis.situationIndex as number), - situationIndex: 0, - })), - ) + $effect(() => { + if ( + matomoConfig !== undefined && + Object.keys(shared.parametricReform).length !== 0 + ) { + trackParametricReform(shared.parametricReform, matomoConfig.prependDomain) } - return situation - } + }) function matomo({ prependDomain, @@ -455,529 +109,13 @@ })() } - async function sendBudgetSimulationDemand() { - const budgetVariableName = $displayMode?.parametersVariableName - if ( - budgetVariableName === undefined || - !budgetVariablesName.has(budgetVariableName) - ) { - console.error( - `Budget calculation for variable ${budgetVariableName} is not available`, - ) - $budgetSimulation = undefined - return - } - const variableName = budgetVariableNameByVariableName[budgetVariableName] - const variableConfig: BudgetVariable = budgetVariablesConfig[variableName] - const simulationBody = buildBudgetDemandBody( - variableName, - variableConfig.outputVariables, - variableConfig.quantileBaseVariable, - variableConfig.quantileCompareVariables, - false, - ) - const urlString = "/budgets/demands" - const res = await fetch(urlString, { - body: JSON.stringify( - { - displayMode: $displayMode, - email: $requestedSimulationEmail, - request: Object.fromEntries( - Object.entries($parametricReform).filter(([parameterName]) => - budgetEditableParametersName.has(parameterName), - ), - ), - simulation: simulationBody, - }, - null, - 2, - ), - headers: { - Accept: "application/json", - "Content-Type": "application/json; charset=utf-8", - }, - method: "POST", - }) - if (!res.ok) { - $requestedSimulationEmail = undefined - console.error( - `Error ${ - res.status - } while sending a simulation request at ${urlString}\n\n${await res.text()}`, - ) - return - } - $requestedSimulationEmail = undefined - } - - async function sendTestCasesSimulation( - calculationName: CalculationName, - { - period, - parametric_reform, - reform, - situation, - variables, - }: TestCasesCalculationInput, - ) { - const completeVariableSummaryByName = - reformName === undefined - ? variableSummaryByName - : variableSummaryByNameByReformName[reformName] - try { - // Note: crypto.randomUUID() is not supported by Safari before version 15.4. - // const token = crypto.randomUUID() - const token = uuidV4() - testCasesAbortControllers[calculationName].abort() - testCasesAbortControllers[calculationName] = new AbortController() - const response = await fetch("/test_cases", { - body: JSON.stringify({ - period, - parametric_reform, - reform, - situation, - title: calculationName, - token, - variables, - }), - headers: { - Accept: "application/json", - "Content-Type": "application/json; charset=utf-8", - }, - method: "POST", - signal: testCasesAbortControllers[calculationName].signal, - }) - if (!response.ok) { - console.error( - `Error while submitting test case simulation:\n${response.status} ${response.statusText}`, - ) - console.error(await response.text()) - return - } - const { evaluations } = (await response.json()) as { - evaluations: [ - { - entity: string - name: string - value: VariableValues - }, - ] - token: string - } - - let calculation = $calculationByName[calculationName] as Calculation - - let updatedEvaluationByNameArray = - calculation.situationIndex === undefined - ? $evaluationByNameArray - : [...$evaluationByNameArray] - for (const { - entity: entityKey, - name: variableName, - value, - } of evaluations) { - // Round returned values if unit of variable is a currency. - const variable = completeVariableSummaryByName[variableName] - const roundedValue = - variable.unit === "currency" || variable.unit?.startsWith("currency-") - ? value.map((valueAtIndex) => Math.round(valueAtIndex as number)) - : value - - const entity = entityByKey[entityKey] - - if (calculation.situationIndex === undefined) { - // Variable has been computed for all test cases. - - // Count total population of test cases. - let testCasesPopulationCount = 0 - for (const situation of $testCases) { - const entitySituation = situation[entity.key_plural as string] ?? {} - const situationPopulationCount = Object.keys(entitySituation).length - testCasesPopulationCount += situationPopulationCount - } - - // Split evaluation.value vector for each situation. - { - let testCasesPopulationIndex = 0 - $valuesByCalculationNameByVariableNameArray = - $valuesByCalculationNameByVariableNameArray.map( - ( - valuesByCalculationNameByVariableName, - situationIndex, - ): ValuesByCalculationNameByVariableName => { - const situation = $testCases[situationIndex] - const entitySituation = - situation[entity.key_plural as string] ?? {} - let values = [] - for ( - let index = testCasesPopulationIndex, vectorIndex = 0; - vectorIndex < $vectorLength; - index += testCasesPopulationCount, vectorIndex++ - ) { - for (const situationPersonIndex of Object.keys( - entitySituation, - ).keys()) { - values.push(roundedValue[index + situationPersonIndex]) - } - } - testCasesPopulationIndex += - Object.keys(entitySituation).length - const valuesByCalculationName = - valuesByCalculationNameByVariableName[variableName] ?? {} - return { - ...valuesByCalculationNameByVariableName, - [variableName]: { - ...valuesByCalculationName, - [calculationName]: values, - }, - } - }, - ) - } - } else { - // Variable has been computed for a single test case. - - const updatedValuesByCalculationNameByVariableNameArray = [ - ...$valuesByCalculationNameByVariableNameArray, - ] - const valuesByCalculationNameByVariableName = { - ...updatedValuesByCalculationNameByVariableNameArray[ - calculation.situationIndex - ], - } - const valuesByCalculationName = - valuesByCalculationNameByVariableName[variableName] ?? {} - valuesByCalculationNameByVariableName[variableName] = { - ...valuesByCalculationName, - [calculationName]: roundedValue, - } - updatedValuesByCalculationNameByVariableNameArray[ - calculation.situationIndex - ] = valuesByCalculationNameByVariableName - $valuesByCalculationNameByVariableNameArray = - updatedValuesByCalculationNameByVariableNameArray - } - - // Update evaluations for decompositions. - const calculationNonVirtualVariablesName = - calculationName === "law" - ? nonVirtualVariablesName - : (nonVirtualVariablesNameByReformName[reformName as string] ?? - nonVirtualVariablesName) - if (calculationNonVirtualVariablesName.includes(variableName)) { - if (calculation.situationIndex === undefined) { - // Variable has been computed for all test cases. - - // First, update delta and values of evaluations. - updatedEvaluationByNameArray = updatedEvaluationByNameArray.map( - (evaluationByName, situationIndex): EvaluationByName => { - const situation = $testCases[situationIndex] - const values = $valuesByCalculationNameByVariableNameArray[ - situationIndex - ][variableName][calculationName] as VariableValues - const entitySituation = - situation[entity.key_plural as string] ?? {} - const situationPopulationCount = - Object.keys(entitySituation).length - let delta = new Array($vectorLength).fill(0) - for (const situationPersonIndex of Object.keys( - entitySituation, - ).keys()) { - for ( - let index = situationPersonIndex, vectorIndex = 0; - vectorIndex < $vectorLength; - index += situationPopulationCount, vectorIndex++ - ) { - delta[vectorIndex] += values[index] - } - } - const evaluation = evaluationByName[variableName] - const calculationEvaluationByName = - evaluation?.calculationEvaluationByName ?? {} - return { - ...evaluationByName, - [variableName]: { - ...(evaluation ?? {}), - calculationEvaluationByName: { - ...calculationEvaluationByName, - [calculationName]: { - ...(calculationEvaluationByName[calculationName] ?? {}), - delta, - deltaAtVectorIndex: - delta[situation.slider?.vectorIndex ?? 0] ?? 0, - }, - }, - fromOpenFisca: true, - }, - } - }, - ) - } else { - // Variable has been computed for a single test case. - - // First, update delta and values of evaluations. - const situation = $testCases[calculation.situationIndex] - const values = $valuesByCalculationNameByVariableNameArray[ - calculation.situationIndex - ][variableName][calculationName] as VariableValues - const entitySituation = situation[entity.key_plural as string] ?? {} - const situationPopulationCount = Object.keys(entitySituation).length - let delta = new Array($vectorLength).fill(0) - for (const situationPersonIndex of Object.keys( - entitySituation, - ).keys()) { - for ( - let index = situationPersonIndex, vectorIndex = 0; - vectorIndex < $vectorLength; - index += situationPopulationCount, vectorIndex++ - ) { - delta[vectorIndex] += values[index] - } - } - const evaluationByName = { - ...updatedEvaluationByNameArray[calculation.situationIndex], - } - const evaluation = evaluationByName[variableName] - const calculationEvaluationByName = - evaluation?.calculationEvaluationByName ?? {} - evaluationByName[variableName] = { - ...(evaluation ?? {}), - calculationEvaluationByName: { - ...calculationEvaluationByName, - [calculationName]: { - ...(calculationEvaluationByName[calculationName] ?? {}), - delta, - deltaAtVectorIndex: - delta[situation.slider?.vectorIndex ?? 0] ?? 0, - }, - }, - fromOpenFisca: true, - } - } - } - } - - // Update deltaSums of evaluations from their new delta. - if (calculation.situationIndex === undefined) { - $evaluationByNameArray = updatedEvaluationByNameArray.map( - (evaluationByName, situationIndex) => - updateEvaluations( - $decompositionByName, - evaluationByName, - $testCases[situationIndex].slider?.vectorIndex ?? 0, - $vectorLength, - waterfalls, - ), - ) - } else { - updatedEvaluationByNameArray[calculation.situationIndex] = - updateEvaluations( - $decompositionByName, - updatedEvaluationByNameArray[calculation.situationIndex], - $testCases[calculation.situationIndex].slider?.vectorIndex ?? 0, - $vectorLength, - waterfalls, - ) - $evaluationByNameArray = updatedEvaluationByNameArray - } - - calculation = { ...calculation } - delete calculation.running - $calculationByName = { - ...$calculationByName, - [calculationName]: calculation, - } - } catch (e) { - if (e?.name === "AbortError") { - return - } - } - } - - function updateBillName(billName: string | undefined): void { - // Note: We always use the reform (aka "bill") decomposition, because it is more - // complete than decomposition before reform (aka "law" or "revaluation") and - // "amendment"has always the same decomposition as "bill", because it is a - // parametric-only reform. - if (billName !== currentBillName) { - currentBillName = billName - const newDecompositionByName = buildDecompositionByNameFromCore( - currentBillName === undefined - ? decompositionCoreByName - : (decompositionCoreByNameByReformName[currentBillName] ?? - decompositionCoreByName), - ) as DecompositionByName - const oldDecompositionByName = $decompositionByName - for (const [name, decomposition] of Object.entries( - newDecompositionByName, - )) { - if (oldDecompositionByName[name]?.open) { - decomposition.open = true - } - } - $decompositionByName = newDecompositionByName - } - } - - function updateOnSliderChange(situations: Situation[]): void { - let changedAxesSituationIndex: number[] = [] - let evaluationByNameArrayChanged = false - const updatedEvaluationByNameArray = [...$evaluationByNameArray] - for (const [situationIndex, situation] of situations.entries()) { - const slider = situation.slider - if (slider === undefined) { - if (axisBySituationIndex[situationIndex] !== undefined) { - axisBySituationIndex = { ...axisBySituationIndex } - delete axisBySituationIndex[situationIndex] - changedAxesSituationIndex.push(situationIndex) - - // Change only vectorIndexes content, because vectorIndexes reactivity - // is not needed. - vectorIndexes[situationIndex] = 0 - } - } else { - const currentAxis = axisBySituationIndex[situationIndex] - if ( - currentAxis === undefined || - currentAxis.name !== slider.name || - currentAxis.min !== slider.min || - currentAxis.max !== slider.max - ) { - const entity = entityByKey[slider.entity] - - let previousSituationsPopulationCount = 0 - for (const situation of $testCases.slice(0, situationIndex)) { - const entitySituation = situation[entity.key_plural as string] ?? {} - const populationCount = Object.keys(entitySituation).length - previousSituationsPopulationCount += populationCount - } - const axis = { - count: 101, - index: - previousSituationsPopulationCount + - indexOfSituationPopulationId(entity, situation, slider.id), - max: slider.max, - min: slider.min, - name: slider.name, - period: $year.toString(), // Previous years are added later. - situationIndex: previousSituationsPopulationCount, - } - axisBySituationIndex = { - ...axisBySituationIndex, - [situationIndex]: axis, - } - changedAxesSituationIndex.push(situationIndex) - } - - const vectorIndex = slider.vectorIndex - if (vectorIndex !== vectorIndexes[situationIndex]) { - // Change only vectorIndexes content, because vectorIndexes reactivity - // is not needed. - vectorIndexes[situationIndex] = vectorIndex - - // Update evaluations. - let evaluationByName = updatedEvaluationByNameArray[situationIndex] - const updatedEvaluationByName = updateEvaluationsVectorIndex( - evaluationByName, - vectorIndex, - ) - if (updatedEvaluationByName !== evaluationByName) { - updatedEvaluationByNameArray[situationIndex] = - updatedEvaluationByName - evaluationByNameArrayChanged = true - } - } - } - } - - if (changedAxesSituationIndex.length > 0) { - const parallelAxes = Object.entries(axisBySituationIndex) - .sort(([situationIndex1], [situationIndex2]) => - situationIndex1.localeCompare(situationIndex2), - ) - .map(([, axis]) => axis) - .reduce((parallelAxes, axis) => { - // Add previous years of each axis. - const year = parseInt(axis.period as string) - parallelAxes.push( - { - ...axis, - period: (year - 2).toString(), - }, - { - ...axis, - period: (year - 1).toString(), - }, - axis, - ) - return parallelAxes - }, [] as Axis[]) - $axes = parallelAxes.length === 0 ? [] : [parallelAxes] - - let updatedVectorLength = 1 - for (const parallelAxes of $axes) { - // All the parallel axes have the same count. - const axis = parallelAxes[0] - updatedVectorLength *= axis.count - } - $vectorLength = updatedVectorLength - - for (const situationIndex of changedAxesSituationIndex) { - updatedEvaluationByNameArray[situationIndex] = updateEvaluations( - $decompositionByName, - updatedEvaluationByNameArray[situationIndex], - vectorIndexes[situationIndex], - $vectorLength, - waterfalls, - ) - evaluationByNameArrayChanged = true - } - - // Launch calculations if the axes have not been deleted (ie $vectorLength > 1). - const situationIndex = - changedAxesSituationIndex.length === 1 - ? changedAxesSituationIndex[0] - : undefined - requestAllTestCasesCalculations(situationIndex ?? null) - } - - if (evaluationByNameArrayChanged) { - $evaluationByNameArray = updatedEvaluationByNameArray - } - } - - onNavigate((navigation) => { - if (navigation.to?.route?.id !== navigation.from?.route?.id) { - $navbarConfig = getNavbarConfig(navigation.to?.route?.id ?? "/") - } - }) - let { fetch } = $derived(data) - let { url } = $derived($page) - run(() => { - updateBillName(reformName) - }) - run(() => { - updateOnSliderChange($testCases) - }) - run(() => { - if (browser) { - // Launch first simulation. - requestAllTestCasesCalculations(null) - } + $effect(() => { + updateOnSliderChange(shared.testCases) }) - // run(() => { - // if ( - // $requestedCalculations.budgetVariableName !== undefined && - // $requestedCalculations.budgetCalculationNames !== undefined - // ) { - // const { /*budgetCalculationNames, */ budgetVariableName } = - // $requestedCalculations - // const newRequestedCalculations = { ...$requestedCalculations } - // delete newRequestedCalculations.budgetCalculationNames - // $requestedCalculations = newRequestedCalculations - // calculateBudget(budgetVariableName /*, budgetCalculationNames */) // Don't await - // $requestedSimulationSent = false - // } + // TODO svelte5: find out why requestAllTestCasesCalculations makes the UI lag + // onMount(() => { + // // Launch first simulation. + // requestAllTestCasesCalculations(null) // }) $effect(() => { if ( @@ -988,29 +126,14 @@ calculateBudget( requestedCalculations.budgetVariableName /*, budgetCalculationNames */, ) // Don't await - $requestedSimulationSent = false + shared.requestedSimulationSent = false } }) - run(() => { - if ($requestedSimulationEmail !== undefined) { + $effect(() => { + if (shared.requestedSimulationEmail !== undefined) { sendBudgetSimulationDemand() } }) - // run(() => { - // if ( - // browser && - // Object.keys($requestedCalculations.situationIndexByCalculationName) - // .length > 0 - // ) { - // const situationIndexByCalculationName = - // $requestedCalculations.situationIndexByCalculationName - // $requestedCalculations = { - // ...$requestedCalculations, - // situationIndexByCalculationName: {}, - // } - // calculateTestCases(situationIndexByCalculationName) - // } - // }) $effect(() => { if ( Object.keys(requestedCalculations.situationIndexByCalculationName) @@ -1019,37 +142,27 @@ $inspect("avant", requestedCalculations.situationIndexByCalculationName) const situationIndexByCalculationName = requestedCalculations.situationIndexByCalculationName - untrack(() => { - requestedCalculations.situationIndexByCalculationName = {} - }) + requestedCalculations.situationIndexByCalculationName = {} $inspect("après", requestedCalculations.situationIndexByCalculationName) calculateTestCases(situationIndexByCalculationName) } $inspect("fin") }) - run(() => { - if (browser && $requestedVariablesNameToCalculate !== undefined) { - calculateTestCasesAdditionalVariables($requestedVariablesNameToCalculate) - $requestedVariablesNameToCalculate = undefined - } - }) - run(() => { - if (browser && matomoConfig !== undefined && $page) { - trackPageView(url, matomoConfig.prependDomain) + $effect(() => { + if (shared.requestedVariablesNameToCalculate !== undefined) { + calculateTestCasesAdditionalVariables( + shared.requestedVariablesNameToCalculate, + ) + shared.requestedVariablesNameToCalculate = undefined } }) - run(() => { - if ( - browser && - matomoConfig !== undefined && - Object.keys($parametricReform).length !== 0 - ) { - trackParametricReform($parametricReform, matomoConfig.prependDomain) - } + $effect(() => { + shared.showTutorial = localStorage.getItem("hideTutorial") === null }) - run(() => { - if (browser) { - $showTutorial = localStorage.getItem("hideTutorial") === null + + onNavigate((navigation) => { + if (navigation.to?.route?.id !== navigation.from?.route?.id) { + shared.navbarConfig = getNavbarConfig(navigation.to?.route?.id ?? "/") } }) </script> @@ -1075,7 +188,7 @@ <div class="flex flex-col flex-nowrap md:h-screen" - class:h-screen={$searchActive} + class:h-screen={shared.searchActive} > <NavBar /> diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 7d677202f4fe2d3c3ff46e6abd4cdde2c6956a3a..5cc6a390b1788090751e325a7fa87ae82e3f2f27 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -17,23 +17,18 @@ import * as htmlToImage from "html-to-image" import introJs from "intro.js" import type { IntroStep } from "intro.js/src/core/steps" - import { getContext, setContext, mount, unmount } from "svelte" + import { getContext, setContext, mount, unmount, untrack } from "svelte" import { quartOut } from "svelte/easing" import { writable, type Writable } from "svelte/store" import { fade, fly } from "svelte/transition" import type { PageData } from "./$types" - import { browser } from "$app/environment" import { goto } from "$app/navigation" import { page } from "$app/stores" import { auditSimulationHash } from "$lib/auditors/hashes" import { auditQueryArray, auditQuerySingleton } from "$lib/auditors/queries" - import type { BudgetSimulation } from "$lib/budgets" - import { - requestAllBudgetCalculations, - type RequestedCalculations, - } from "$lib/calculations.svelte" + import { requestAllBudgetCalculations } from "$lib/calculations.svelte" import BudgetLayout from "$lib/components/budget/BudgetLayout.svelte" import BudgetConnexionModal from "$lib/components/BudgetConnexionModal.svelte" import BudgetSimulationSharingModal from "$lib/components/BudgetSimulationSharingModal.svelte" @@ -60,8 +55,6 @@ getDecompositionParentName, getLatestCalculation, waterfalls, - type DecompositionByName, - type EvaluationByName, } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" import { entityByKey } from "$lib/entities" @@ -71,7 +64,14 @@ trackTestCaseEdit, } from "$lib/matomo" import publicConfig from "$lib/public_config" - import type { ParametricReform } from "$lib/reforms" + import { + billActive, + billName, + budgetDate, + date, + shared, + year, + } from "$lib/shared.svelte" import { publishTestCaseSimulation } from "$lib/simulations" import { getPopulationReservedKeys, type Situation } from "$lib/situations" import type { TabsConfig } from "$lib/tabs" @@ -82,7 +82,6 @@ budgetVariablesConfig, budgetVariablesName, budgetVariablesNameRelated, - type ValuesByCalculationNameByVariableName, variableSummaryByName, variableSummaryByNameByReformName, } from "$lib/variables" @@ -91,27 +90,16 @@ data: PageData } - const { appTitle, baseUrl, reformName } = publicConfig + const { appTitle, baseUrl } = publicConfig + let { data }: Props = $props() let animationEndedTimeoutId = undefined - const billActive = getContext("billActive") as Writable<boolean> - const budgetSimulation = getContext("budgetSimulation") as Writable< - BudgetSimulation | undefined - > - const budgetDate = getContext("budgetDate") as Writable<string> let clipboardElement: HTMLElement = $state() - const date = getContext("date") as Writable<string> - const decompositionByName = getContext( - "decompositionByName", - ) as Writable<DecompositionByName> let displayMode: DisplayMode = $state({ testCasesIndex: [], waterfallName: waterfalls[0].name, }) - const displayModeWritable = getContext("displayMode") as Writable< - DisplayMode | undefined - > const dispositifsTypes = [ { title: "Impôt sur le revenu", @@ -217,22 +205,12 @@ ], }, ] - const evaluationByNameArray = getContext("evaluationByNameArray") as Writable< - EvaluationByName[] - > const formatLongOrdinalSup = (n: number) => { const rule = ordinalPluralRules.select(n) const suffix = longOrdinalSuffixes.get(rule) return `${n}<sup>${suffix}</sup>` } let highlightDecomposition = false - const inputInstantsByVariableNameArray = getContext( - "inputInstantsByVariableNameArray", - ) as Writable< - Array<{ - [name: string]: Set<string> - }> - > let isBudgetConnexionModalOpen = $state(false) let isBudgetSharingModalOpen = $state(false) let isTestCaseSelectModalOpen = $state(false) @@ -243,14 +221,6 @@ setContext("newSelfTargetAProps", newSelfTargetAProps) setContext("onCopyParameterLink", onCopyParameterLink) const ordinalPluralRules = new Intl.PluralRules("fr-FR", { type: "ordinal" }) - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const searchActive = getContext("searchActive") as Writable<boolean> - const searchVariableName = getContext("searchVariableName") as Writable< - string | undefined - > - const showTutorial = getContext("showTutorial") as Writable<boolean> const tabsConfig = $state({ defaultTab: "fiche_de_paie", tabs: [ @@ -272,20 +242,13 @@ }, ], } as TabsConfig) - const testCases = getContext("testCases") as Writable<Situation[]> - const testCasesIndex = getContext("testCasesIndex") as Writable<number[]> let testCaseSharingModal: { open: boolean; token?: string } = $state({ open: false, }) - const valuesByCalculationNameByVariableNameArray = getContext( - "valuesByCalculationNameByVariableNameArray", - ) as Writable<ValuesByCalculationNameByVariableName[]> const variableModalOpen: Writable<boolean> = writable(false) setContext("variableModalOpen", variableModalOpen) let variablesHistory: string[] = $state([]) - const waterfall = getContext("waterfall") as Writable<Waterfall> let windowInnerWidth: number | undefined = $state(undefined) - const year = getContext("year") as Writable<number> function auditSimulationQuery( audit: Audit, @@ -336,7 +299,7 @@ auditStringToNumber, auditInteger, auditTest( - (index) => index >= 0 && index < $testCases.length, + (index) => index >= 0 && index < shared.testCases.length, "Ce numéro ne correspond à aucun cas type", ), ), @@ -368,7 +331,7 @@ auditStringToNumber, auditInteger, auditTest( - (index) => index >= 0 && index < $testCases.length, + (index) => index >= 0 && index < shared.testCases.length, "Ce numéro ne correspond à aucun cas type", ), ), @@ -469,7 +432,7 @@ // function changeBillName({ target }: Event) { // const { value } = target as HTMLSelectElement - // reformName = value === "undefined" ? undefined : value + // billName = value === "undefined" ? undefined : value // $requestedCalculations = requestTestCasesCalculation( // $requestedCalculations, // "bill", @@ -494,13 +457,6 @@ changeSituation(situationIndex, situation) } - function changeDecompositionByName( - situationIndex: number, - newDecompositionByName: DecompositionByName, - ): void { - $decompositionByName = newDecompositionByName - } - function changeInputInstantsByVariableName( situationIndex: number, inputInstantsByVariableName: { @@ -508,17 +464,18 @@ }, ): void { const newInputInstantsByVariableNameArray = [ - ...$inputInstantsByVariableNameArray, + ...shared.inputInstantsByVariableNameArray, ] newInputInstantsByVariableNameArray[situationIndex] = inputInstantsByVariableName - $inputInstantsByVariableNameArray = newInputInstantsByVariableNameArray + shared.inputInstantsByVariableNameArray = + newInputInstantsByVariableNameArray } function changeSituation(situationIndex: number, situation: Situation): void { - const situations = [...$testCases] + const situations = [...shared.testCases] situations[situationIndex] = situation - $testCases = situations + shared.testCases = situations } function changeTestCasesIndex(testCasesIndex: number[]): void { @@ -594,8 +551,8 @@ ...validQueryDisplayMode, ...validHashDisplayMode, } - $testCasesIndex = displayMode.testCasesIndex - $waterfall = waterfalls.find( + shared.testCasesIndex = displayMode.testCasesIndex + shared.waterfall = waterfalls.find( ({ name }) => name === displayMode.waterfallName, ) @@ -607,7 +564,7 @@ ) { // Only called once, when the page has been loaded from the server // with a budget=true query parameter - $budgetSimulation = undefined + shared.budgetSimulation = undefined setTimeout(() => { requestAllBudgetCalculations(displayMode.parametersVariableName) }, 300) @@ -617,19 +574,13 @@ if (displayMode.parametersVariableName !== undefined) { for (let variableName = displayMode.parametersVariableName; ; ) { const parentVariableName = getDecompositionParentName( - $decompositionByName, + shared.decompositionByName, variableName, ) if (parentVariableName === undefined) { break } - $decompositionByName = { - ...$decompositionByName, - [parentVariableName]: { - ...$decompositionByName[parentVariableName], - open: true, - }, - } + shared.decompositionByName[parentVariableName].open = true variableName = parentVariableName } } @@ -671,7 +622,7 @@ { noScroll: true }, ) - $searchVariableName = undefined + shared.searchVariableName = undefined } function newSelfTargetAProps(urlPath: string): SelfTargetAProps { @@ -702,19 +653,20 @@ parameterHash: parameterName, mobileLaw: true, }, - inputInstantsByVariableNameArray: $inputInstantsByVariableNameArray.map( - (inputInstantsByVariableName) => - Object.fromEntries( - Object.entries(inputInstantsByVariableName).map( - ([variableName, inputInstants]) => [ - variableName, - [...inputInstants], - ], + inputInstantsByVariableNameArray: + shared.inputInstantsByVariableNameArray.map( + (inputInstantsByVariableName) => + Object.fromEntries( + Object.entries(inputInstantsByVariableName).map( + ([variableName, inputInstants]) => [ + variableName, + [...inputInstants], + ], + ), ), - ), - ), - parametricReform: $parametricReform, - testCases: $testCases, + ), + parametricReform: shared.parametricReform, + testCases: shared.testCases, }), ) @@ -766,10 +718,10 @@ const token = await publishTestCaseSimulation( blob, - $displayModeWritable!, - $inputInstantsByVariableNameArray, - $parametricReform, - $testCases, + shared.displayMode!, + shared.inputInstantsByVariableNameArray, + shared.parametricReform, + shared.testCases, ) if (token !== undefined) { @@ -795,25 +747,31 @@ } } let { user } = $derived(data) - run(() => { + if ($page.url.searchParams || $page.url.hash) { ensureValidDisplayMode($page.url.searchParams, $page.url.hash) + } + $effect(() => { + if ($page.url.searchParams || $page.url.hash) { + untrack(() => + ensureValidDisplayMode($page.url.searchParams, $page.url.hash), + ) + } }) - run(() => { - $displayModeWritable = displayMode + $effect(() => { + shared.displayMode = displayMode }) - run(() => { + $effect(() => { if (displayMode?.parameterHash !== undefined) { clearParameterHash() } }) - run(() => { + $effect(() => { if ( - browser && windowInnerWidth !== undefined && windowInnerWidth >= 768 && - $showTutorial + shared.showTutorial ) { - $showTutorial = false + shared.showTutorial = false localStorage.setItem("hideTutorial", "true") introJs() .setOptions({ @@ -937,8 +895,8 @@ .start() } }) - run(() => { - loadSearchVariable($searchVariableName) + $effect(() => { + loadSearchVariable(shared.searchVariableName) }) let mobileLawTab = $derived( windowInnerWidth !== undefined && @@ -946,7 +904,7 @@ displayMode.mobileLaw, ) let modificationsAmendmentCount = $derived( - Object.keys($parametricReform).length, + Object.keys(shared.parametricReform).length, ) let showBudgetBlurred = $derived( displayMode.budget && @@ -954,21 +912,21 @@ (!mobileLawTab || (windowInnerWidth !== undefined && windowInnerWidth >= 768)) && displayMode.parametersVariableName !== undefined && - Object.keys($parametricReform).filter((parameterName) => + Object.keys(shared.parametricReform).filter((parameterName) => budgetEditableParametersName.has(parameterName), ).length > 0 && - Object.keys($parametricReform).length > 0 && - $budgetSimulation !== undefined && - ($budgetSimulation.errors == null || - $budgetSimulation.errors.length === 0) && - $budgetSimulation?.result?.amendement === undefined, + Object.keys(shared.parametricReform).length > 0 && + shared.budgetSimulation !== undefined && + (shared.budgetSimulation.errors == null || + shared.budgetSimulation.errors.length === 0) && + shared.budgetSimulation?.result?.amendement === undefined, ) let showPublicSimulationPanel = $derived( displayMode.budget && !mobileLawTab && displayMode.parametersVariableName !== undefined && - Object.keys($parametricReform).length > 0 && - $budgetSimulation?.result?.amendement !== undefined, + Object.keys(shared.parametricReform).length > 0 && + shared.budgetSimulation?.result?.amendement !== undefined, ) run(() => { tabsConfig.tabs[0].disabled = @@ -992,7 +950,7 @@ <main class="relative flex h-full flex-1 overflow-x-clip bg-graph-paper md:overflow-hidden" - class:overflow-hidden={$searchActive} + class:overflow-hidden={shared.searchActive} > <div class="flex w-full flex-col {displayMode.edit !== undefined @@ -1070,7 +1028,7 @@ class="flex min-w-0 flex-[1_0_100%] overflow-y-scroll bg-white shadow-lg transition-transform duration-500 ease-out-quart md:z-10 md:!h-full md:flex-[0_0_33.3%] md:translate-x-0 md:overflow-visible md:!p-0" class:-translate-x-full={!mobileLawTab} class:!h-[calc(100vh-96px)]={!mobileLawTab} - class:pb-24={$testCasesIndex.length === 1 && + class:pb-24={shared.testCasesIndex.length === 1 && displayMode.parametersVariableName === undefined} id="situation_left_part_law" > @@ -1103,7 +1061,7 @@ Sélectionner un impôt, une cotisation ou une prestation : </h2> - {#if $billActive} + {#if billActive} <PlfVariablesListIntro {displayMode} /> {/if} {#key displayMode.budget} @@ -1267,7 +1225,7 @@ in:fade={{ delay: 150, duration: 150 }} > <VariableReferredParameters - date={$budgetDate} + date={budgetDate} {displayMode} name={displayMode.parametersVariableName} /> @@ -1304,7 +1262,7 @@ class="rounded border-1 text-xs" on:blur={changeBillName} on:change={changeBillName} - value={reformName} + value={billName} > <option value={undefined}>Pas de projet/proposition de loi</option> {#each metadata.reforms as { label, name }} @@ -1383,9 +1341,9 @@ class="absolute -bottom-4 right-2 z-30 flex items-center gap-2 rounded border border-le-bleu bg-white px-5 py-2 text-sm font-bold uppercase tracking-[0.085em] text-le-bleu shadow-lg transition-all duration-200 ease-out-back hover:bg-gray-100 active:bg-gray-200 disabled:scale-90 disabled:opacity-0 lg:right-5 xl:right-10" onclick={() => (isBudgetSharingModalOpen = true)} disabled={displayMode.parametersVariableName === undefined || - $budgetSimulation === undefined || - ($budgetSimulation.errors != null && - $budgetSimulation.errors.length > 0)} + shared.budgetSimulation === undefined || + (shared.budgetSimulation.errors != null && + shared.budgetSimulation.errors.length > 0)} > <span class="hidden lg:inline">Enregistrer / partager</span> <iconify-icon class="text-lg" icon="ri:share-fill" @@ -1426,7 +1384,7 @@ > {#if !budgetVariablesName.has(displayMode.parametersVariableName) && !budgetVariablesNameRelated.has(displayMode.parametersVariableName)} {@const variable = - $decompositionByName[ + shared.decompositionByName[ displayMode.parametersVariableName ] ?? variableSummaryByName[ @@ -1443,7 +1401,7 @@ <!--Panneau indicateur que d'autres dispositifs sont disponibles en budgétaire--> <WithoutBudgetCard {displayMode} {variable} /> - {:else if $budgetSimulation === undefined && !budgetVariablesNameRelated.has(displayMode.parametersVariableName)} + {:else if shared.budgetSimulation === undefined && !budgetVariablesNameRelated.has(displayMode.parametersVariableName)} <!--Skeleton Loader qui apparait quand le budget charge--> {@const budgetVariableName = displayMode.parametersVariableName !== undefined @@ -1455,11 +1413,11 @@ ? { ...budgetVariablesConfig[budgetVariableName], label: - $decompositionByName[budgetVariableName] + shared.decompositionByName[budgetVariableName] ?.short_label ?? variableSummaryByName[budgetVariableName] ?.short_label ?? - $decompositionByName[budgetVariableName] + shared.decompositionByName[budgetVariableName] ?.label ?? variableSummaryByName[budgetVariableName] ?.label ?? @@ -1471,7 +1429,7 @@ {budgetVariable?.label} </div> <SkeletonLoaderBudget /> - {:else if $budgetSimulation !== undefined && $budgetSimulation.errors != null && $budgetSimulation.errors.length > 0} + {:else if shared.budgetSimulation !== undefined && shared.budgetSimulation.errors != null && shared.budgetSimulation.errors.length > 0} <div class="mx-auto flex w-fit flex-col items-center gap-10 rounded-lg border border-gray-200 bg-white p-10 shadow-md md:mt-32" > @@ -1479,14 +1437,14 @@ class="text-6xl" icon="material-symbols:report-outline" ></iconify-icon> - {#each $budgetSimulation.errors as error} + {#each shared.budgetSimulation.errors as error} <span>{error}</span> {/each} </div> {:else} <BudgetLayout blur={showBudgetBlurred} - budgetSimulation={$budgetSimulation} + budgetSimulation={shared.budgetSimulation} {displayMode} /> {/if} @@ -1543,7 +1501,7 @@ </div> {/key} {:else} - {#key $testCasesIndex.length || displayMode.parametersVariableName} + {#key shared.testCasesIndex.length || displayMode.parametersVariableName} <div class={!displayMode.edit ? "px-3 py-4 md:px-5 xl:px-10 xl:py-8" @@ -1555,39 +1513,34 @@ {displayMode} bind:isOpen={isTestCaseSelectModalOpen} on:changeTestCasesIndex - year={$year} + {year} /> - {#if $testCasesIndex.length > 0} + {#if shared.testCasesIndex.length > 0} <!-- Cas types avec leur waterfall --> - {#if $testCasesIndex.length === 1} + {#if shared.testCasesIndex.length === 1} <TestCaseView - decompositionByName={$decompositionByName} {displayMode} {highlightDecomposition} - on:changeDecompositionByName={({ detail }) => - changeDecompositionByName($testCasesIndex[0], detail)} on:changeSituation={({ detail }) => - changeSituation($testCasesIndex[0], detail)} + changeSituation(shared.testCasesIndex[0], detail)} on:changeTestCasesIndex={({ detail }) => changeTestCasesIndex(detail)} on:changeTestCaseToEditIndex={({ detail }) => { changeTestCaseToEditIndex(detail) trackTestCaseEdit() }} - situation={$testCases[$testCasesIndex[0]]} - situationIndex={$testCasesIndex[0]} + situation={shared.testCases[shared.testCasesIndex[0]]} + situationIndex={shared.testCasesIndex[0]} {tabsConfig} - valuesByCalculationNameByVariableName={$valuesByCalculationNameByVariableNameArray[ - $testCasesIndex[0] + valuesByCalculationNameByVariableName={shared + .valuesByCalculationNameByVariableNameArray[ + shared.testCasesIndex[0] ]} - year={$year} + {year} /> {:else} <TestCaseCompareView - decompositionByName={$decompositionByName} {displayMode} - on:changeDecompositionByName={({ detail }) => - changeDecompositionByName($testCasesIndex[0], detail)} on:changeSituation={changeCompareSituation} on:changeTestCasesIndex={({ detail }) => changeTestCasesIndex(detail)} @@ -1595,8 +1548,8 @@ changeTestCaseToEditIndex(detail) trackTestCaseEdit() }} - situationsToCompareIndex={$testCasesIndex} - year={$year} + situationsToCompareIndex={shared.testCasesIndex} + {year} /> {/if} {:else} @@ -1609,15 +1562,15 @@ </h2> {:else} {@const variableSummary = - reformName === undefined + billName === undefined ? variableSummaryByName[ displayMode.parametersVariableName ] - : variableSummaryByNameByReformName[reformName][ + : variableSummaryByNameByReformName[billName][ displayMode.parametersVariableName ]} {#if variableSummary !== undefined} - {@const indexedTestCases = $testCases + {@const indexedTestCases = shared.testCases .map((situation, situationIndex) => ({ situation, situationIndex, @@ -1666,11 +1619,11 @@ : 0 : 1, )} - {@const nonNullTestCases = $testCases.reduce( + {@const nonNullTestCases = shared.testCases.reduce( (arr, curr, index) => { const latestCalculationValue = getLatestCalculation( - $evaluationByNameArray[index][ + shared.evaluationByNameArray[index][ displayMode.parametersVariableName ]?.calculationEvaluationByName, )?.deltaAtVectorIndex @@ -1694,7 +1647,7 @@ [], )} {@const shortLabel = - $decompositionByName[ + shared.decompositionByName[ displayMode.parametersVariableName ]?.short_label ?? variableSummary.short_label} @@ -1741,10 +1694,11 @@ mode="select" {situation} situationIndex={index} - valuesByCalculationNameByVariableName={$valuesByCalculationNameByVariableNameArray[ + valuesByCalculationNameByVariableName={shared + .valuesByCalculationNameByVariableNameArray[ index ]} - year={$year} + {year} /> </div> <div @@ -1758,9 +1712,8 @@ {shortLabel} : <VariableValueChange bold={true} - evaluationByName={$evaluationByNameArray[ - index - ]} + evaluationByName={shared + .evaluationByNameArray[index]} inline name={displayMode.parametersVariableName} /> @@ -1834,7 +1787,7 @@ <TestCasePictos classes="[&>svg]:w-7 [&>svg]:h-7 col-span-3 last:odd:col-start-3 justify-center" situation={situations[0]} - year={$year} + {year} /> </div> {/if} @@ -1858,9 +1811,8 @@ {/if} <VariableValueChange bold={true} - evaluationByName={$evaluationByNameArray[ - index - ]} + evaluationByName={shared + .evaluationByNameArray[index]} inline name={displayMode.parametersVariableName} /> @@ -1887,16 +1839,16 @@ > {#if displayMode.parametersVariableName !== undefined} {@const variableSummary = - reformName === undefined + billName === undefined ? variableSummaryByName[ displayMode.parametersVariableName ] - : variableSummaryByNameByReformName[reformName][ + : variableSummaryByNameByReformName[billName][ displayMode.parametersVariableName ]} {#if displayMode.testCasesIndex.length > 0} {@const shortLabel = - $decompositionByName[ + shared.decompositionByName[ displayMode.parametersVariableName ]?.short_label ?? variableSummary.short_label} <a @@ -2015,21 +1967,20 @@ </div> <div class="overflow-y-scroll"> <TestCaseEdit - date={$date} - inputInstantsByVariableName={$inputInstantsByVariableNameArray[ + {date} + inputInstantsByVariableName={shared.inputInstantsByVariableNameArray[ displayMode.edit ] ?? {}} on:changeInputInstantsByVariableName={({ detail }) => changeInputInstantsByVariableName(displayMode.edit, detail)} on:changeSituation={({ detail }) => changeSituation(displayMode.edit, detail)} - situation={$testCases[displayMode.edit] ?? {}} + situation={shared.testCases[displayMode.edit] ?? {}} situationIndex={displayMode.edit} - valuesByCalculationNameByVariableName={$valuesByCalculationNameByVariableNameArray[ - displayMode.edit - ]} + valuesByCalculationNameByVariableName={shared + .valuesByCalculationNameByVariableNameArray[displayMode.edit]} variableName={displayMode.variableName} - year={$year} + {year} /> <!-- #75 !253 : cache le panneau, on pourrait le mettre en tooltip/modale plus tard --> <!-- <div class="m-4 rounded bg-le-bleu-light p-2 text-gray-700 shadow-md">--> @@ -2137,15 +2088,14 @@ {#if displayMode.testCasesIndex !== undefined && displayMode.testCasesIndex.length > 0} {@const testCaseIndex = displayMode.testCasesIndex[0]} - {@const testCase = $testCases[testCaseIndex]} + {@const testCase = shared.testCases[testCaseIndex]} <TestCaseScreenshotLayout - decompositionByName={$decompositionByName} {displayMode} - evaluationByNameArray={$evaluationByNameArray} + evaluationByNameArray={shared.evaluationByNameArray} {testCase} {testCaseIndex} - valuesByCalculationNameByVariableNameArray={$valuesByCalculationNameByVariableNameArray} - year={$year} + valuesByCalculationNameByVariableNameArray={shared.valuesByCalculationNameByVariableNameArray} + {year} /> {/if} diff --git a/src/routes/accueil/+page.svelte b/src/routes/accueil/+page.svelte index b74ae8ddcfe9468cbc01bfa93e61b4e31a38c5b0..3a6e2a358c994cb0e73b55f25e9ee4eab1bfac11 100644 --- a/src/routes/accueil/+page.svelte +++ b/src/routes/accueil/+page.svelte @@ -10,9 +10,6 @@ import { getContext } from "svelte" import type { Writable } from "svelte/store" - import type { PageData } from "./$types" - - import { browser } from "$app/environment" import { goto } from "$app/navigation" import { page } from "$app/stores" import { auditQuerySingleton } from "$lib/auditors/queries" @@ -28,8 +25,8 @@ import { waterfalls, withLinkedVariableNames } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" import { trackSearchVariable } from "$lib/matomo" - import type { NavbarConfig } from "$lib/navbar" import publicConfig from "$lib/public_config" + import { billActive, shared } from "$lib/shared.svelte" import { newSimulationUrl } from "$lib/urls" const { appTitle } = publicConfig @@ -38,8 +35,6 @@ testCasesIndex: [], waterfallName: waterfalls[0].name, }) - const billActive = getContext("billActive") as Writable<boolean> - const navbarConfig = getContext("navbarConfig") as Writable<NavbarConfig> const searchVariableName = getContext("searchVariableName") as Writable< string | undefined > @@ -188,29 +183,27 @@ { noScroll: true }, ) - $searchVariableName = undefined + shared.searchVariableName = undefined } - run(() => { + $effect(() => { ensureValidDisplayMode($page.url.searchParams) }) - run(() => { - loadSearchVariable($searchVariableName) + $effect(() => { + loadSearchVariable(shared.searchVariableName) }) let variablesCount = $derived(withLinkedVariableNames.length - 100) - run(() => { - if (browser) { - if (displayMode.parametersVariableName !== undefined) { - document.body.classList.add("overflow-hidden") - $navbarConfig = { - position: "fixed", - showSearch: true, - } - } else { - document.body.classList.remove("overflow-hidden") - $navbarConfig = { - position: "fixed", - showSearch: false, - } + $effect(() => { + if (displayMode.parametersVariableName !== undefined) { + document.body.classList.add("overflow-hidden") + shared.navbarConfig = { + position: "fixed", + showSearch: true, + } + } else { + document.body.classList.remove("overflow-hidden") + shared.navbarConfig = { + position: "fixed", + showSearch: false, } } }) @@ -227,8 +220,8 @@ <main class="h-full bg-yellow-50 pt-24 md:pt-12 2xl:pt-14"> <section class="fond relative py-10 before:absolute before:inset-x-0 before:top-0 before:h-[50vh] before:bg-gradient-to-b before:from-[#9AAAB4] before:to-transparent" - class:max-h-none={$billActive} - class:h-[78vh]={!$billActive} + class:max-h-none={billActive} + class:h-[78vh]={!billActive} > <div class="relative z-10 flex h-full flex-col items-center justify-evenly text-base md:text-lg 2xl:text-2xl" @@ -321,7 +314,7 @@ ></iconify-icon></a > </div> - {#if $billActive} + {#if billActive} <div> <h2 class="mt-10 text-xl font-bold xl:text-2xl 2xl:text-3xl"> Consulter les impacts du PLF / PLFSS 2025 : diff --git a/src/routes/auth/prepare/+page.svelte b/src/routes/auth/prepare/+page.svelte index 3105b2e435a6b5956e650f27fbe5bd03db4663a6..abd35ca512d1c4fcee8a61d6f226ce7c688ad5c9 100644 --- a/src/routes/auth/prepare/+page.svelte +++ b/src/routes/auth/prepare/+page.svelte @@ -1,49 +1,32 @@ <script lang="ts"> - import { getContext, onMount } from "svelte" - import type { Writable } from "svelte/store" + import { onMount } from "svelte" import { goto } from "$app/navigation" import { page } from "$app/stores" - import type { DisplayMode } from "$lib/displays" - import type { ParametricReform } from "$lib/reforms" - import type { Situation } from "$lib/situations" - - const displayMode = getContext("displayMode") as Writable< - DisplayMode | undefined - > - const inputInstantsByVariableNameArray = getContext( - "inputInstantsByVariableNameArray", - ) as Writable< - Array<{ - [name: string]: Set<string> - }> - > - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const testCases = getContext("testCases") as Writable<Situation[]> + import { shared } from "$lib/shared.svelte" onMount(() => { localStorage.setItem( "simulatorState", JSON.stringify({ displayMode: { - ...$displayMode, + ...shared.displayMode, parameterHash: undefined, }, - inputInstantsByVariableNameArray: $inputInstantsByVariableNameArray.map( - (inputInstantsByVariableName) => - Object.fromEntries( - Object.entries(inputInstantsByVariableName).map( - ([variableName, inputInstants]) => [ - variableName, - [...inputInstants], - ], + inputInstantsByVariableNameArray: + shared.inputInstantsByVariableNameArray.map( + (inputInstantsByVariableName) => + Object.fromEntries( + Object.entries(inputInstantsByVariableName).map( + ([variableName, inputInstants]) => [ + variableName, + [...inputInstants], + ], + ), ), - ), - ), - parametricReform: $parametricReform, - testCases: $testCases, + ), + parametricReform: shared.parametricReform, + testCases: shared.testCases, }), ) diff --git a/src/routes/auth/restore_state/+page.svelte b/src/routes/auth/restore_state/+page.svelte index 1197e8e70a3ba8d1ff1771a4b36464cefbd8f736..f9952e9038859580c6c5b9feb8df2d7367c55a9a 100644 --- a/src/routes/auth/restore_state/+page.svelte +++ b/src/routes/auth/restore_state/+page.svelte @@ -1,42 +1,17 @@ <script lang="ts"> - import { getContext, onMount } from "svelte" - import type { Writable } from "svelte/store" + import { onMount } from "svelte" import { goto } from "$app/navigation" - import { - requestAllTestCasesCalculations, - type RequestedCalculations, - } from "$lib/calculations.svelte" - import type { EvaluationByName } from "$lib/decompositions" - import type { ParametricReform } from "$lib/reforms" - import type { Situation } from "$lib/situations" + import { requestAllTestCasesCalculations } from "$lib/calculations.svelte" + import { shared } from "$lib/shared.svelte" import { newSimulationUrl } from "$lib/urls" - import type { ValuesByCalculationNameByVariableName } from "$lib/variables" - - const evaluationByNameArray = getContext("evaluationByNameArray") as Writable< - EvaluationByName[] - > - const inputInstantsByVariableNameArray = getContext( - "inputInstantsByVariableNameArray", - ) as Writable< - Array<{ - [name: string]: Set<string> - }> - > - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const testCases = getContext("testCases") as Writable<Situation[]> - const valuesByCalculationNameByVariableNameArray = getContext( - "valuesByCalculationNameByVariableNameArray", - ) as Writable<ValuesByCalculationNameByVariableName[]> onMount(() => { const simulatorStateString = localStorage.getItem("simulatorState") if (simulatorStateString != undefined && simulatorStateString != null) { const simulatorState = JSON.parse(simulatorStateString) - $inputInstantsByVariableNameArray = + shared.inputInstantsByVariableNameArray = simulatorState.inputInstantsByVariableNameArray.map( (inputInstantsByVariableName) => Object.fromEntries( @@ -48,16 +23,16 @@ ), ), ) - $parametricReform = simulatorState.parametricReform - $testCases = simulatorState.testCases.map((situation) => { + shared.parametricReform = simulatorState.parametricReform + shared.testCases = simulatorState.testCases.map((situation) => { // Delete slider from each situation, because the activation of a slider // at initialization is not implemented. delete situation.slider return situation }) - $evaluationByNameArray = new Array($testCases.length).fill({}) - $valuesByCalculationNameByVariableNameArray = new Array( - $testCases.length, + shared.evaluationByNameArray = new Array(shared.testCases.length).fill({}) + shared.valuesByCalculationNameByVariableNameArray = new Array( + shared.testCases.length, ).fill({}) requestAllTestCasesCalculations(null) diff --git a/src/routes/budgets/simulations/+page.svelte b/src/routes/budgets/simulations/+page.svelte index 0dd8f4cf08d6b613783c58710c52f592ef78a288..1a7f89e94a9622707f801b0fba27dd70f8b3d215 100644 --- a/src/routes/budgets/simulations/+page.svelte +++ b/src/routes/budgets/simulations/+page.svelte @@ -1,17 +1,13 @@ <script lang="ts"> - import { getContext, onMount } from "svelte" - import type { Writable } from "svelte/store" + import { onMount } from "svelte" import { fade } from "svelte/transition" import { goto } from "$app/navigation" - import type { DisplayMode } from "$lib/displays" + import { shared } from "$lib/shared.svelte" import type { CachedSimulation } from "$lib/simulations" import { newSimulationUrl } from "$lib/urls" let cachedSimulations: CachedSimulation[] = $state([]) - const displayMode = getContext("displayMode") as Writable< - DisplayMode | undefined - > let loading = $state(true) onMount(async () => { @@ -34,7 +30,11 @@ class="inline-flex items-center gap-2 rounded-md border border-gray-700 bg-white px-5 py-2 text-sm font-bold uppercase tracking-[0.085em] text-gray-700 hover:bg-gray-100 active:bg-gray-200" title="Retour au simulateur" onclick={() => - goto($displayMode === undefined ? "/" : newSimulationUrl($displayMode))} + goto( + shared.displayMode === undefined + ? "/" + : newSimulationUrl(shared.displayMode), + )} > <iconify-icon class="align-[-0.25rem] text-xl" icon="ri-arrow-left-line" ></iconify-icon> diff --git a/src/routes/budgets/simulations/[simulation]/+page.svelte b/src/routes/budgets/simulations/[simulation]/+page.svelte index 3f1411b347f2bee3745be4f86ecf0b7536f88ba7..56382f6efbb5e4ea37e23f3ede65f503c6990a0f 100644 --- a/src/routes/budgets/simulations/[simulation]/+page.svelte +++ b/src/routes/budgets/simulations/[simulation]/+page.svelte @@ -1,12 +1,11 @@ <script lang="ts"> - import { getContext, onMount } from "svelte" - import type { Writable } from "svelte/store" + import { onMount } from "svelte" import type { PageData } from "./$types" import { goto } from "$app/navigation" - import type { ParametricReform } from "$lib/reforms" import { newSimulationUrl } from "$lib/urls" + import { shared } from "$lib/shared.svelte" interface Props { data: PageData @@ -14,14 +13,10 @@ let { data }: Props = $props() - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - let { simulation } = $derived(data) onMount(() => { - $parametricReform = simulation.parametricReform + shared.parametricReform = simulation.parametricReform goto( simulation.displayMode === undefined ? "/" diff --git a/src/routes/test/+page.svelte b/src/routes/test/+page.svelte new file mode 100644 index 0000000000000000000000000000000000000000..5543ba48bed86fe0dd2dea125f4b9556b1c0d0e9 --- /dev/null +++ b/src/routes/test/+page.svelte @@ -0,0 +1,45 @@ +<script lang="ts"> + import TestComponent from "$lib/components/TestComponent.svelte" + + let variable = $state({ + a: 1, + b: 2, + }) + + setTimeout(() => { + variable = undefined + }, 3000) +</script> + +{#if variable !== undefined} + <TestComponent {variable} /> +{:else} + success +{/if} + +<!--<script lang="ts">--> +<!-- import { untrack } from "svelte"--> + +<!-- let x = $state(0)--> +<!-- let variable = $state({--> +<!-- a: 1,--> +<!-- b: 2,--> +<!-- })--> + +<!-- $effect(() => {--> +<!-- if (x) {--> +<!-- untrack(() => {--> +<!-- variable = {--> +<!-- a: 3,--> +<!-- b: 4,--> +<!-- }--> + +<!-- $inspect(variable.a)--> +<!-- })--> +<!-- }--> +<!-- })--> +<!--</script>--> + +<!--<button onclick={() => x++}> Plus </button>--> + +<!--{x}--> diff --git a/src/routes/test_cases/simulations/[simulation]/+page.svelte b/src/routes/test_cases/simulations/[simulation]/+page.svelte index 9b532594c567b384e8d6caac62096023dd209eaa..2022b6eb57f173034c709b6a642ad983a8df62bf 100644 --- a/src/routes/test_cases/simulations/[simulation]/+page.svelte +++ b/src/routes/test_cases/simulations/[simulation]/+page.svelte @@ -1,25 +1,15 @@ <script lang="ts"> - import { getContext, onMount } from "svelte" - import type { Writable } from "svelte/store" + import { onMount } from "svelte" import type { PageData } from "./$types" import { goto } from "$app/navigation" import { page } from "$app/stores" - import { - requestAllTestCasesCalculations, - type RequestedCalculations, - } from "$lib/calculations.svelte" + import { requestAllTestCasesCalculations } from "$lib/calculations.svelte" import OpenGraph from "$lib/components/transverse_pages/OpenGraph.svelte" - import type { - EvaluationByName, - DecompositionByName, - } from "$lib/decompositions" import publicConfig from "$lib/public_config" - import type { ParametricReform } from "$lib/reforms" - import type { Situation } from "$lib/situations" + import { shared } from "$lib/shared.svelte" import { newSimulationUrl } from "$lib/urls" - import type { ValuesByCalculationNameByVariableName } from "$lib/variables" import { variableSummaryByName } from "$lib/variables.js" interface Props { @@ -27,33 +17,13 @@ } const { baseUrl } = publicConfig - let { data }: Props = $props() - const decompositionByName = getContext( - "decompositionByName", - ) as Writable<DecompositionByName> - const evaluationByNameArray = getContext("evaluationByNameArray") as Writable< - EvaluationByName[] - > - const inputInstantsByVariableNameArray = getContext( - "inputInstantsByVariableNameArray", - ) as Writable< - Array<{ - [name: string]: Set<string> - }> - > - const parametricReform = getContext( - "parametricReform", - ) as Writable<ParametricReform> - const testCases = getContext("testCases") as Writable<Situation[]> - const valuesByCalculationNameByVariableNameArray = getContext( - "valuesByCalculationNameByVariableNameArray", - ) as Writable<ValuesByCalculationNameByVariableName[]> + let { data }: Props = $props() let { simulation } = $derived(data) onMount(() => { - $inputInstantsByVariableNameArray = + shared.inputInstantsByVariableNameArray = simulation.inputInstantsByVariableNameArray.map( (inputInstantsByVariableName) => Object.fromEntries( @@ -65,11 +35,11 @@ ), ), ) - $parametricReform = simulation.parametricReform - $testCases = simulation.testCases - $evaluationByNameArray = new Array($testCases.length).fill({}) - $valuesByCalculationNameByVariableNameArray = new Array( - $testCases.length, + shared.parametricReform = simulation.parametricReform + shared.testCases = simulation.testCases + shared.evaluationByNameArray = new Array(shared.testCases.length).fill({}) + shared.valuesByCalculationNameByVariableNameArray = new Array( + shared.testCases.length, ).fill({}) requestAllTestCasesCalculations(null) @@ -87,12 +57,12 @@ {#if simulation.displayMode.parametersVariableName !== undefined} <OpenGraph - description="➡️ Voici l'impact du dispositif «{$decompositionByName[ + description="➡️ Voici l'impact du dispositif «{shared.decompositionByName[ simulation.displayMode.parametersVariableName ]?.short_label ?? variableSummaryByName[simulation.displayMode.parametersVariableName] ?.short_label ?? - $decompositionByName[simulation.displayMode.parametersVariableName] + shared.decompositionByName[simulation.displayMode.parametersVariableName] ?.label ?? variableSummaryByName[simulation.displayMode.parametersVariableName] ?.label}» sur ce cas type ! Le simulateur permet aussi de connaître les impôts, cotisations et prestations sociales de ce foyer." diff --git a/src/routes/variables/[variable]/+page.svelte b/src/routes/variables/[variable]/+page.svelte index 01152a9770bf653db5c081bd2fcc22c78d5ca83a..1274c8ca85be5d47bc3badab9bbdfd974ee20b7a 100644 --- a/src/routes/variables/[variable]/+page.svelte +++ b/src/routes/variables/[variable]/+page.svelte @@ -1,15 +1,14 @@ <script lang="ts"> import { run } from "svelte/legacy" - import { getContext, setContext } from "svelte" - import type { Writable } from "svelte/store" + import { setContext } from "svelte" import type { PageData } from "./$types" import { goto } from "$app/navigation" import VariableView from "$lib/components/variables/VariableView.svelte" - import type { DisplayMode } from "$lib/displays" import publicConfig from "$lib/public_config" + import { shared } from "$lib/shared.svelte" import type { Situation } from "$lib/situations" import { newSelfTargetAProps, newSimulationUrl } from "$lib/urls" import type { ValuesByCalculationNameByVariableName } from "$lib/variables" @@ -19,25 +18,8 @@ } const { appTitle } = publicConfig - let { data }: Props = $props() - const date = getContext("date") as Writable<string> - const displayMode = getContext("displayMode") as Writable< - DisplayMode | undefined - > - const inputInstantsByVariableNameArray = getContext( - "inputInstantsByVariableNameArray", - ) as Writable< - Array<{ - [name: string]: Set<string> - }> - > - const testCasesIndex = getContext("testCasesIndex") as Writable<number[]> - const testCases = getContext("testCases") as Writable<Situation[]> - const valuesByCalculationNameByVariableNameArray = getContext( - "valuesByCalculationNameByVariableNameArray", - ) as Writable<ValuesByCalculationNameByVariableName[]> - const year = getContext("year") as Writable<number> + let { data }: Props = $props() setContext("newSelfTargetAProps", newSelfTargetAProps) @@ -46,25 +28,26 @@ }): void { if ( inputInstantsByVariableName === - $inputInstantsByVariableNameArray[testCaseIndex] + shared.inputInstantsByVariableNameArray[testCaseIndex] ) { return } const updatedInputInstantsByVariableNameArray = [ - ...$inputInstantsByVariableNameArray, + ...shared.inputInstantsByVariableNameArray, ] updatedInputInstantsByVariableNameArray[testCaseIndex] = inputInstantsByVariableName - $inputInstantsByVariableNameArray = updatedInputInstantsByVariableNameArray + shared.inputInstantsByVariableNameArray = + updatedInputInstantsByVariableNameArray } function updateSituation(situation: Situation): void { - if (situation === $testCases[testCaseIndex]) { + if (situation === shared.testCases[testCaseIndex]) { return } - const situations = [...$testCases] + const situations = [...shared.testCases] situations[testCaseIndex] = situation - $testCases = situations + shared.testCases = situations } function updateValuesByCalculationNameByVariableName( @@ -72,33 +55,33 @@ ): void { if ( valuesByCalculationNameByVariableName === - $valuesByCalculationNameByVariableNameArray[testCaseIndex] + shared.valuesByCalculationNameByVariableNameArray[testCaseIndex] ) { return } const updatedValuesByCalculationNameByVariableNameArray = [ - ...$valuesByCalculationNameByVariableNameArray, + ...shared.valuesByCalculationNameByVariableNameArray, ] updatedValuesByCalculationNameByVariableNameArray[testCaseIndex] = valuesByCalculationNameByVariableName - $valuesByCalculationNameByVariableNameArray = + shared.valuesByCalculationNameByVariableNameArray = updatedValuesByCalculationNameByVariableNameArray } let { variable } = $derived(data) - let testCaseIndex = $derived($testCasesIndex?.[0] ?? 0) + let testCaseIndex = $derived(shared.testCasesIndex?.[0] ?? 0) let inputInstantsByVariableName run(() => { inputInstantsByVariableName = - $inputInstantsByVariableNameArray[testCaseIndex] + shared.inputInstantsByVariableNameArray[testCaseIndex] }) let situation run(() => { - situation = $testCases[testCaseIndex] + situation = shared.testCases[testCaseIndex] }) let valuesByCalculationNameByVariableName run(() => { valuesByCalculationNameByVariableName = - $valuesByCalculationNameByVariableNameArray[testCaseIndex] + shared.valuesByCalculationNameByVariableNameArray[testCaseIndex] }) run(() => { updateInputInstantsByVariableName(inputInstantsByVariableName) @@ -122,20 +105,22 @@ <button class="ml-5 mt-5 inline-flex cursor-pointer items-center rounded bg-gray-200 p-2 pr-3 text-sm text-black shadow-md hover:bg-gray-300 active:bg-gray-400 md:ml-10" onclick={() => - goto($displayMode === undefined ? "/" : newSimulationUrl($displayMode))} + goto( + shared.displayMode === undefined + ? "/" + : newSimulationUrl(shared.displayMode), + )} > <iconify-icon class="text-2xl" icon="ri-arrow-left-line"></iconify-icon> <span class="ml-3">Retour au simulateur</span> </button> <VariableView - date={$date} editable bind:inputInstantsByVariableName bind:situation situationIndex={testCaseIndex} bind:valuesByCalculationNameByVariableName {variable} - year={$year} /> </div> </main> diff --git a/src/routes/variables/[variable]/inputs/[date]/+page.svelte b/src/routes/variables/[variable]/inputs/[date]/+page.svelte index 531801b710417017b5a8f447e5047c21e533afd4..3544b8f8ff42503d87f4919267f14a6bb7832935 100644 --- a/src/routes/variables/[variable]/inputs/[date]/+page.svelte +++ b/src/routes/variables/[variable]/inputs/[date]/+page.svelte @@ -1,17 +1,16 @@ <script lang="ts"> import { run } from "svelte/legacy" - import { getContext, setContext } from "svelte" - import type { Writable } from "svelte/store" + import { setContext } from "svelte" import type { PageData } from "./$types" import { page } from "$app/stores" import VariableReferredInputs from "$lib/components/variables/VariableReferredInputs.svelte" + import publicConfig from "$lib/public_config" import type { Situation } from "$lib/situations" + import { shared, year } from "$lib/shared.svelte" import { newSelfTargetAProps } from "$lib/urls" - import type { ValuesByCalculationNameByVariableName } from "$lib/variables" - import publicConfig from "$lib/public_config" interface Props { data: PageData @@ -20,20 +19,6 @@ const { appTitle } = publicConfig let { data }: Props = $props() - const inputInstantsByVariableNameArray = getContext( - "inputInstantsByVariableNameArray", - ) as Writable< - Array<{ - [variableName: string]: Set<string> - }> - > - const testCasesIndex = getContext("testCasesIndex") as Writable<number[]> - const testCases = getContext("testCases") as Writable<Situation[]> - const valuesByCalculationNameByVariableNameArray = getContext( - "valuesByCalculationNameByVariableNameArray", - ) as Writable<ValuesByCalculationNameByVariableName[]> - const year = getContext("year") as Writable<number> - setContext("newSelfTargetAProps", newSelfTargetAProps) function updateInputInstantsByVariableName(inputInstantsByVariableName: { @@ -41,45 +26,39 @@ }): void { if ( inputInstantsByVariableName === - $inputInstantsByVariableNameArray[testCaseIndex] + shared.inputInstantsByVariableNameArray[testCaseIndex] ) { return } const newInputInstantsByVariableNameArray = [ - ...$inputInstantsByVariableNameArray, + ...shared.inputInstantsByVariableNameArray, ] newInputInstantsByVariableNameArray[testCaseIndex] = inputInstantsByVariableName - $inputInstantsByVariableNameArray = newInputInstantsByVariableNameArray + shared.inputInstantsByVariableNameArray = + newInputInstantsByVariableNameArray } function updateSituation(situation: Situation): void { - if (situation === $testCases[testCaseIndex]) { + if (situation === shared.testCases[testCaseIndex]) { return } - const situations = [...$testCases] + const situations = [...shared.testCases] situations[testCaseIndex] = situation - $testCases = situations + shared.testCases = situations } let { inputs, variable } = $derived(data) - let testCaseIndex = $derived($testCasesIndex?.[0] ?? 0) + let testCaseIndex = $derived(shared.testCasesIndex?.[0] ?? 0) let params = $derived($page.params) let date = $derived(params.date) let name = $derived(params.variable) - let inputInstantsByVariableName - run(() => { - inputInstantsByVariableName = - $inputInstantsByVariableNameArray[testCaseIndex] - }) - let situation - run(() => { - situation = $testCases[testCaseIndex] - }) - let valuesByCalculationNameByVariableName - run(() => { - valuesByCalculationNameByVariableName = - $valuesByCalculationNameByVariableNameArray[testCaseIndex] - }) + let inputInstantsByVariableName = $derived( + shared.inputInstantsByVariableNameArray[testCaseIndex], + ) + let situation = $derived(shared.testCases[testCaseIndex]) + let valuesByCalculationNameByVariableName = $derived( + shared.valuesByCalculationNameByVariableNameArray[testCaseIndex], + ) run(() => { updateInputInstantsByVariableName(inputInstantsByVariableName) }) @@ -101,6 +80,6 @@ situationIndex={testCaseIndex} bind:valuesByCalculationNameByVariableName {variable} - year={$year} + {year} /> </main> diff --git a/src/routes/variables/[variable]/parameters/[date]/+page.svelte b/src/routes/variables/[variable]/parameters/[date]/+page.svelte index 0e21126461669d777d10f270c6fc71ff0daaf1db..59f36b02494347362d8e88565d1a43aedde4e316 100644 --- a/src/routes/variables/[variable]/parameters/[date]/+page.svelte +++ b/src/routes/variables/[variable]/parameters/[date]/+page.svelte @@ -1,6 +1,5 @@ <script lang="ts"> - import { getContext, setContext } from "svelte" - import type { Writable } from "svelte/store" + import { setContext } from "svelte" import { page } from "$app/stores" import VariableReferredParameters from "$lib/components/variables/VariableReferredParameters.svelte" @@ -8,14 +7,14 @@ import type { DisplayMode } from "$lib/displays" import publicConfig from "$lib/public_config" import { newSelfTargetAProps } from "$lib/urls" + import { shared } from "$lib/shared.svelte" const { appTitle } = publicConfig let displayMode: DisplayMode = $derived({ - testCasesIndex: $testCasesIndex ?? [0], + testCasesIndex: shared.testCasesIndex ?? [0], variableName: name, waterfallName: waterfalls[0].name, }) - const testCasesIndex = getContext("testCasesIndex") as Writable<number[]> setContext("newSelfTargetAProps", newSelfTargetAProps) diff --git a/src/routes/variables/[variable]/xlsx/+page.svelte b/src/routes/variables/[variable]/xlsx/+page.svelte index ce22a6925dd79f5f19ac3d44531a60f35ad5343e..6b3cb48160c7754ad125aea9097e5ebd7ecfb2e4 100644 --- a/src/routes/variables/[variable]/xlsx/+page.svelte +++ b/src/routes/variables/[variable]/xlsx/+page.svelte @@ -14,8 +14,6 @@ type RoleBase, type Variable, } from "@openfisca/json-model" - import { getContext } from "svelte" - import type { Writable } from "svelte/store" import { v4 as uuidV4 } from "uuid" import XLSX from "xlsx-js-style" @@ -26,10 +24,10 @@ import { entityByKey, personEntityKey } from "$lib/entities" import { rootParameter } from "$lib/parameters" import publicConfig from "$lib/public_config" + import { shared, year } from "$lib/shared.svelte" import { getPopulationReservedKeys, // type Axis, - type Situation, type SituationWithAxes, } from "$lib/situations" import { @@ -44,6 +42,7 @@ } const { apiBaseUrls } = publicConfig + let { data }: Props = $props() type ValuesByCalculationNameByPeriodByVariableName = { @@ -53,23 +52,14 @@ } // const axes = getContext("axes") as Writable<Axis[][]> - const inputInstantsByVariableNameArray = getContext( - "inputInstantsByVariableNameArray", - ) as Writable< - Array<{ - [name: string]: Set<string> - }> - > - const testCases = getContext("testCases") as Writable<Situation[]> - const year = getContext("year") as Writable<number> async function calculate(variables: Variable[]) { const aggregatedSituation: SituationWithAxes = {} const entities = Object.values(entityByKey) // Aggregate every situations into a single one without calculated variables. - for (const [situationIndex, situation] of $testCases.entries()) { + for (const [situationIndex, situation] of shared.testCases.entries()) { const inputInstantsByVariableName = - $inputInstantsByVariableNameArray[situationIndex] + shared.inputInstantsByVariableNameArray[situationIndex] const situationPrefix = `Cas type n°${situationIndex + 1} | ` for (const entity of entities) { let entitySituation = situation[entity.key_plural as string] @@ -134,8 +124,8 @@ } } - // if ($axes.length > 0) { - // aggregatedSituation.axes = $axes + // if (axes.length > 0) { + // aggregatedSituation.axes = axes // } // Note: crypto.randomUUID() is not supported by Safari before version 15.4. @@ -146,7 +136,7 @@ const apiBaseUrl = apiBaseUrls[apiBaseUrlIndex] const response = await fetch(new URL("simulations", apiBaseUrl), { body: JSON.stringify({ - period: $year.toString(), + period: year.toString(), situation: aggregatedSituation, title: "law", token, @@ -219,7 +209,7 @@ "Tableur créé automatiquement par le simulateur socio-fiscal Leximpact, utilisant le moteur libre OpenFisca", Company: "Assemblée nationale", CreatedDate: new Date(), - Title: `${lastVariable.short_label ?? lastVariable.name} en ${$year}`, + Title: `${lastVariable.short_label ?? lastVariable.name} en ${year}`, } workbook.Workbook = { Names: [], @@ -253,7 +243,7 @@ } = {} const rowIndexByEntityKey: { [entityKey: string]: number } = {} const tableByEntityKey: { [key: string]: Array<Array<unknown>> } = {} // Array of rows - for (const [situationIndex, situation] of $testCases.entries()) { + for (const [situationIndex, situation] of shared.testCases.entries()) { const groupInfosByIdByEntityKey: { [entityKey: string]: { [groupId: string]: { groupIndex: number; personsId: string[] } @@ -361,7 +351,7 @@ } } table = tableByEntityKey[entityKey] = [header] - for (const [situationIndex, situation] of $testCases.entries()) { + for (const [situationIndex, situation] of shared.testCases.entries()) { let entitySituation = situation[entity.key_plural as string] if (entitySituation === undefined) { continue @@ -615,7 +605,7 @@ XLSX.writeFile( workbook, - `leximpact_${requestedVariableName}_${$year}.xlsx`, + `leximpact_${requestedVariableName}_${year}.xlsx`, { compression: true }, ) }