diff --git a/src/lib/components/impacts_view/test_cases_view/test_case_selected/PaySlipViewOld.svelte b/src/lib/components/impacts_view/test_cases_view/test_case_selected/PaySlipViewOld.svelte new file mode 100644 index 0000000000000000000000000000000000000000..067133a29d5f2432ebf74a5ebc755e1fd8c7caa2 --- /dev/null +++ b/src/lib/components/impacts_view/test_cases_view/test_case_selected/PaySlipViewOld.svelte @@ -0,0 +1,705 @@ +<script lang="ts"> + import type { VariableByName } from "@openfisca/json-model" + + import { goto } from "$app/navigation" + import OilSpendingBill from "$lib/components/impacts_view/test_cases_view/test_case_selected/OilSpendingBill.svelte" + import TestCaseGraph from "$lib/components/impacts_view/test_cases_view/test_case_selected/graph/TestCaseGraph.svelte" + import Tooltip from "$lib/components/ui_transverse_components/Tooltip.svelte" + import { + type VisibleDecomposition, + type EvaluationByName, + isChildOrDescendant, + } 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 { revaluationName, shared } from "$lib/shared.svelte" + import { + type ActiveSlider, + getSituationVariableValue, + setSituationVariableValue, + type Situation, + } from "$lib/situations" + import { newSimulationUrl } from "$lib/urls" + import { removeNegativeZero } from "$lib/values" + import { + oilTypes, + type ValuesByCalculationNameByVariableName, + type VariableValue, + } from "$lib/variables" + import LinkedVariables from "$lib/components/impacts_view/test_cases_view/test_case_selected/LinkedVariables.svelte" + import { iterToDepth } from "$lib/iterators" + import StandardOfLiving from "$lib/components/impacts_view/test_cases_view/test_case_selected/StandardOfLiving.svelte" + + interface Props { + displayMode: DisplayMode + evaluationByName: EvaluationByName + showLoader?: boolean + situation: Situation + situationIndex: number + valuesByCalculationNameByVariableName: ValuesByCalculationNameByVariableName + variableSummaryByName: VariableByName + year: number + } + + let { + displayMode, + evaluationByName, + showLoader = true, + situation = $bindable(), + situationIndex, + valuesByCalculationNameByVariableName, + variableSummaryByName, + year, + }: Props = $props() + + const dateFormatter = new Intl.DateTimeFormat("fr-FR", { dateStyle: "full" }) + .format + const deltaFormatter = (value: number): string => + new Intl.NumberFormat("fr-FR", { + currency: "EUR", + maximumFractionDigits: 0, + minimumFractionDigits: 0, + signDisplay: "never", + style: "currency", + }).format(removeNegativeZero(value)) + const firstDeltaFormatter = (value: number): string => + new Intl.NumberFormat("fr-FR", { + currency: "EUR", + maximumFractionDigits: 0, + minimumFractionDigits: 0, + style: "currency", + }).format(removeNegativeZero(value)) + const personEntity = entityByKey[personEntityKey] + + const oilSpendings = oilTypes.map((name) => ({ + depenseTtcVariableName: `depense_${name}_ttc`, + nombreLitresVariableName: `nombre_litres_${name}`, + // prixTtcLitreVariableName: `prix_${name}_hors_remise_ttc_sortie`, + prixTtcLitreVariableName: `prix_${name}_ttc`, + ticpeVariableName: `${name}_ticpe`, + tvaVariableName: `tva_sur_${name}`, + })) + + let useRevaluationInsteadOfLaw = $derived(revaluationName !== undefined) + + let firstCalculationName = $derived( + useRevaluationInsteadOfLaw ? "revaluation" : "law", + ) + + let runningCalculationNames = $derived( + Object.entries(shared.calculationByName) + .filter(([, calculation]) => calculation.running) + .map(([calculationName]) => calculationName), + ) + + let modificationsAmendmentCount = $derived( + Object.keys(shared.parametricReform).length, + ) + + let visibleDecompositions: VisibleDecomposition[] = $state([]) + + $effect(() => { + visibleDecompositions = buildVisibleDecompositions( + shared.decompositionByName, + entityByKey, + evaluationByName, + situation, + variableSummaryByName, + shared.waterfall, + shared.showNulls, + useRevaluationInsteadOfLaw, + shared.vectorLength, + year, + ) + }) + + let personSituation = $derived(situation[personEntity.key_plural!]) + + function getCorrectSimulationUrl(variableName: string) { + const newDisplayMode = + displayMode.edit !== undefined + ? { + ...displayMode, + variableName: variableName, + } + : { + ...displayMode, + mobileLaw: true, + parametersVariableName: variableName, + } + return newSimulationUrl(newDisplayMode) + } + + function getFirstPersonActivity(personSituation) { + const populationId = Object.keys(personSituation).sort( + (populationId1, populationId2) => + populationId1.localeCompare(populationId2), + )[0] + return getVariableValue(situation, "activite", populationId) + } + + function getVariableValue( + situation: Situation, + variableName: string, + populationId: string, + ): VariableValue | undefined { + const variable = variableSummaryByName[variableName] + if (variable === undefined) { + return undefined + } + return getSituationVariableValue(situation, variable, populationId, year) + } + + function removeSituationSlider() { + if (shared.savedSituation !== undefined) { + situation = shared.savedSituation + delete shared.savedSituation + if (shared.savedSituationIndex !== undefined) { + shared.testCases[shared.savedSituationIndex] = situation + delete shared.savedSituationIndex + } + } + } + + function requestAxesCalculation() { + // Ensure that situation of previous curve is not merged with the new one. + if ( + shared.savedSituation !== undefined && + shared.savedSituationIndex !== undefined + ) { + shared.testCases[shared.savedSituationIndex] = shared.savedSituation + } + + shared.savedSituation = situation + shared.savedSituationIndex = situationIndex + situation = structuredClone($state.snapshot(situation)) + shared.testCases[situationIndex] = situation + if ( + situation.sliders?.length === undefined || + situation.sliders?.length <= 0 + ) { + console.error( + "requestAxesCalculation", + "Situation sliders list is undefined", + ) + return + } + + // Get situation + const variable = variableSummaryByName[situation.sliders?.[0].name] + const slider = situation.sliders.find( + (slider) => + slider.entity === variable.entity && slider.name === variable.name, + ) as ActiveSlider | undefined + + if (slider === undefined) { + console.error("requestAxesCalculation", "Slider is undefined") + return + } + + const updatedMin = slider.min + const updatedMax = slider.max + const updatedStepValue = (updatedMax - updatedMin) / 100 ?? slider.stepValue + + const value = getSituationVariableValue( + situation, + variable, + slider.id, + year, + ) as number + + const vectorIndex = Math.max( + 0, + Math.min(100, Math.round(value / updatedStepValue)), + ) + + // Update situation + setSituationVariableValue( + entityByKey, + situation, + variable, + slider.id, + year, + Math.round(updatedStepValue * vectorIndex), + ) + situation.slider = { + ...slider, + min: updatedMin, + max: updatedMax, + stepValue: updatedStepValue, + vectorIndex: vectorIndex, + } + } + + function zoomIn(index: number) { + let visibleDecomposition = visibleDecompositions[index] + if ( + visibleDecomposition === undefined || + visibleDecomposition.visibleChildren === undefined + ) { + return + } + let decomposition = visibleDecomposition.decomposition + if (!decomposition.open) { + shared.decompositionByName[decomposition.name] = { + ...decomposition, + open: true, + } + return + } + } + + function zoomOut(index: number) { + let visibleDecomposition = visibleDecompositions[index] + if ( + visibleDecomposition === undefined || + visibleDecomposition.visibleChildren === undefined + ) { + return + } + let decomposition = visibleDecomposition.decomposition + if (decomposition.open) { + shared.decompositionByName[decomposition.name] = { + ...decomposition, + open: false, + } + return + } + } +</script> + +<!--Ce composant se découpe en 3 zones : +- ZONE 1 : FEUILLE DE PAIE +- ZONE 2 : VARIALBES COMPLÉMENTAIRES À LA FEUILLE DE PAIE +- ZONE 3 : TICKET DE CARBURANT & GRAPHIQUE DES CAS TYPES +--> + +{#if visibleDecompositions.length > 0} + {@const firstPersonActivity = getFirstPersonActivity(personSituation)} + + <!--ZONE 1 : FEUILLE DE PAIE --> + <!-- Bouton affichage des montants nuls --> + <div + class="mx-4 mb-3 flex justify-start" + id="situation_{situationIndex}_waterfall_showall" + > + <label class="inline-flex cursor-pointer items-center"> + <input + type="checkbox" + value="" + class="peer sr-only" + 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-bleu peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-0" + ></div> + <span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300" + >Montrer tous les dispositifs<span + class="hidden sm:inline-flex md:hidden lg:inline-flex" + >, y compris si leur montant est à 0€</span + ></span + > + </label> + </div> + <!-- Feuille de paie--> + <div class="flex justify-between"> + <div class="w-3/5 flex-auto"> + {#each visibleDecompositions as { decomposition, depth, rows, trunk, visibleChildren }, index} + <!-- Ligne de la feuille de paie--> + <div + class="flex min-h-8 items-stretch justify-between border-t border-gray-200 px-4" + class:bg-gray-100={trunk && index !== 0} + class:border-gray-300={trunk && index !== 0} + class:fond={decomposition.name === + displayMode.parametersVariableName || + (displayMode.parametersVariableName && + isChildOrDescendant( + shared.decompositionByName, + decomposition.name, + displayMode.parametersVariableName, + ))} + class:text-lg={decomposition.name === + displayMode.parametersVariableName} + class:items-start={decomposition.name === + displayMode.parametersVariableName} + > + <!-- Colonne 1 "Nom du dispositif"--> + <div class="flex-1 overflow-x-hidden"> + {#each rows as { calculationName }} + {#if calculationName === firstCalculationName} + <div class="flex h-full w-full items-center"> + <!--Si c'est une variable de type tronc, exemple : Rémunération brute --> + {#if trunk && index !== 0} + <!--Les class permettent que le nom de la variable soit caché avec "..." et rendu visible au survol. Le comportement est différent si la variable est sélectionnée--> + <div + class="flex w-full items-center" + class:whitespace-nowrap={decomposition.name !== + displayMode.parametersVariableName} + > + <!-- Nom de la variable tronc, cliquable --> + <a + class="cursor-pointer overflow-x-hidden text-ellipsis text-gray-500 hover:z-20 hover:overflow-x-visible hover:bg-white hover:bg-opacity-90 hover:pr-1 hover:text-le-gris-dispositif-dark hover:underline" + class:font-bold={decomposition.name === + displayMode.parametersVariableName} + class:hover:absolute={decomposition.name !== + displayMode.parametersVariableName} + href={getCorrectSimulationUrl(decomposition.name)} + data-sveltekit-noscroll + >{decomposition.short_label ?? decomposition.label}</a + > + </div> + {:else if visibleChildren === undefined} + <!--Si c'est une variable Non-trunk, leaf-variable : La variable n'a pas de variables enfants et elle n'est pas une variable tronc --> + + <div + class="flex h-full w-full items-center" + class:whitespace-nowrap={decomposition.name !== + displayMode.parametersVariableName} + > + <!--Indentation pour chaque niveau de l'arbre, illustré par une bordure--> + {#each iterToDepth(depth)} + <div + class={`min-h-full border-l-2 bg-white pr-3 ${ + decomposition.name !== + displayMode.parametersVariableName && + !( + displayMode.parametersVariableName && + isChildOrDescendant( + shared.decompositionByName, + decomposition.name, + displayMode.parametersVariableName, + ) + ) + ? "border-gray-400" + : "border-black" + }`} + ></div> + {/each} + <!--Si la variable est obsolète, ou que sa date de relecture est inconnue ou trop ancienne, un picto attention est affiché --> + {#if decomposition.obsolete || decomposition.last_value_still_valid_on === undefined || decomposition.last_value_still_valid_on < (new Date().getFullYear() - 2).toString()} + <Tooltip + allowFlip={false} + arrowClass="bg-gray-100" + widthClass="w-80" + initialPlacement="bottom" + > + <iconify-icon + class="mr-0.5 shadow-none {decomposition.obsolete + ? 'text-[#FF4133]' + : 'text-[#FFAC33]'}" + icon="material-symbols:warning-rounded" + width="16" + height="16" + ></iconify-icon> + {#snippet tooltip()} + <div + class="overflow-hidden rounded-lg border border-gray-200 bg-white shadow-2xl" + > + <div + class="border-b border-gray-200 bg-gray-100 px-3 py-2" + > + <h3 class="font-semibold text-gray-900"> + âš ï¸ Ce dispositif n'est peut-être pas à jour + </h3> + </div> + <div + class="px-3 py-2 text-sm font-light text-gray-500" + > + Dernière relecture : + {#if decomposition.last_value_still_valid_on === undefined} + date indéterminée + {:else}{dateFormatter( + new Date( + decomposition.last_value_still_valid_on, + ), + )} + {/if} + {#if decomposition.obsolete} + ; Obsolète !{/if} + </div> + </div> + {/snippet} + </Tooltip> + {/if} + <!-- Nom de la variable non-trunk, leaf, cliquable --> + <a + class="cursor-pointer overflow-x-hidden text-ellipsis font-serif hover:z-20 hover:overflow-x-visible hover:bg-white hover:bg-opacity-90 hover:pr-1 hover:text-le-gris-dispositif-dark hover:underline" + class:font-bold={decomposition.name === + displayMode.parametersVariableName} + class:hover:text-wrap={decomposition.name !== + displayMode.parametersVariableName} + href={getCorrectSimulationUrl(decomposition.name)} + data-sveltekit-noscroll + >{decomposition.short_label ?? decomposition.label}</a + > + </div> + {:else} + <!-- Si c'est une variable Non-trunk, Non-leaf : La variable a des variables enfants et n'est pas une variable tronc | une flèche à droite permet d'ouvrir les variables enfants--> + + <div + class="flex h-full w-full items-center justify-start" + class:whitespace-nowrap={decomposition.name !== + displayMode.parametersVariableName} + > + <!--Indentation pour chaque niveau de l'arbre, illustré par une bordure--> + {#each iterToDepth(depth)} + <div + class={`min-h-full border-l-2 bg-white pr-3 ${ + decomposition.name !== + displayMode.parametersVariableName && + !( + displayMode.parametersVariableName && + isChildOrDescendant( + shared.decompositionByName, + decomposition.name, + displayMode.parametersVariableName, + ) + ) + ? "border-gray-400" + : "border-black" + }`} + ></div> + {/each} + <button + class="cursor-pointer overflow-x-hidden text-ellipsis text-left font-serif hover:z-20 hover:overflow-x-visible hover:bg-white hover:bg-opacity-90 hover:text-le-gris-dispositif-dark hover:underline" + class:hover:text-wrap={decomposition.name !== + displayMode.parametersVariableName} + class:font-bold={decomposition.name === + displayMode.parametersVariableName} + onclick={() => { + // Non-leaf decomposition node in variable inputs mode => no-link + if (decomposition.open) { + zoomOut(index) + } else { + zoomIn(index) + } + // Leaf decomposition node with parameters in parameters mode => link + if (displayMode.edit === undefined) { + goto(getCorrectSimulationUrl(decomposition.name), { + noScroll: true, + }) + } + }} + data-sveltekit-noscroll + >{decomposition.short_label ?? + decomposition.label}</button + > + <button + class="text-black" + aria-label={decomposition.open + ? "Ouvrir variables enfants" + : "Fermer"} + onclick={() => + decomposition.open ? zoomOut(index) : zoomIn(index)} + > + <iconify-icon + class="align-[-0.25rem] text-lg hover:text-le-gris-dispositif" + icon={decomposition.open + ? "ri-arrow-down-s-line" + : "ri-arrow-right-s-line"} + ></iconify-icon> + </button> + </div> + {/if} + </div> + {/if} + {/each} + </div> + + <!-- Colonne 2 - Valeur du dispositif--> + <div + class="flex flex-nowrap pr-1 sm:items-center md:pr-4" + class:py-1={!trunk} + class:items-end={!trunk} + > + <div class="flex flex-col justify-end sm:flex-row sm:items-center"> + {#each rows as { calculationName, deltaAtVectorIndex }} + <span + class="h-full pl-1 text-right text-sm" + class:content-center={trunk} + class:content-start={!trunk} + > + <!--Loader squelette pendant que la valeur charge --> + {#if showLoader && runningCalculationNames.length > 0} + {#if runningCalculationNames.includes("law") || runningCalculationNames.includes("revaluation")} + <span + class="animate-pulse-2 bg-gray-500 px-1 text-black blur-xs" + > + <span class="text-white blur">value €</span> + </span> + {/if} + {#if runningCalculationNames.includes("bill")} + <span + class="animate-pulse-2 bg-le-rouge-bill px-1 text-black blur-xs" + > + <span class="text-white blur">value €</span> + </span> + {/if} + {#if runningCalculationNames.includes("amendment") && modificationsAmendmentCount > 0} + <span + class="animate-pulse-2 bg-le-jaune px-1 text-black blur-xs" + > + <span class="text-white blur">value €</span> + </span> + {/if} + <!--Valeur du waterfall--> + {:else if trunk && index !== 0} + <span + class=" {calculationName === firstCalculationName + ? rows.find((row) => row.calculationName === 'bill') === + undefined + ? rows.find( + (row) => row.calculationName === 'amendment', + ) === undefined + ? 'text-gray-500' + : 'text-gray-500 line-through-amendment' + : 'text-gray-500 line-through-bill' + : calculationName === 'bill' + ? rows.find( + (row) => row.calculationName === 'amendment', + ) === undefined + ? 'text-le-rouge-bill' + : 'text-le-rouge-bill line-through-amendment' + : 'bg-le-jaune text-gray-500'}" + class:text-base={decomposition.name !== + displayMode.parametersVariableName} + class:text-2xl={decomposition.name === + displayMode.parametersVariableName} + class:font-bold={decomposition.name === + displayMode.parametersVariableName} + ><span + class="content-center pr-1 font-normal text-gray-500" + >=</span + >{firstDeltaFormatter(deltaAtVectorIndex ?? 0)}</span + > + {:else} + <span + class="font-bold {calculationName === firstCalculationName + ? rows.find((row) => row.calculationName === 'bill') === + undefined + ? rows.find( + (row) => row.calculationName === 'amendment', + ) === undefined + ? '' + : 'line-through-amendment' + : 'line-through-bill' + : calculationName === 'bill' + ? rows.find( + (row) => row.calculationName === 'amendment', + ) === undefined + ? 'text-le-rouge-bill' + : 'text-le-rouge-bill line-through-amendment' + : 'bg-le-jaune'}" + class:opacity-20={decomposition.open} + class:text-base={decomposition.name !== + displayMode.parametersVariableName} + class:text-3xl={decomposition.name === + displayMode.parametersVariableName} + class:font-semibold={decomposition.name === + displayMode.parametersVariableName} + >{#if index !== 0}{#if deltaAtVectorIndex < 0}-{:else if deltaAtVectorIndex > 0}+{/if}{/if} + {deltaFormatter(deltaAtVectorIndex ?? 0)}</span + > + {/if} + </span> + {/each} + </div> + </div> + </div> + <!--Affichage des variables liées s'il y en a--> + {#if displayMode.parametersVariableName !== undefined && decomposition.name === displayMode.parametersVariableName} + <LinkedVariables + {displayMode} + {evaluationByName} + {situationIndex} + {variableSummaryByName} + {decomposition} + {depth} + {visibleChildren} + /> + {/if} + {/each} + </div> + </div> + + <!--AJout de la variable niveau de vie--> + {#if shared.waterfall.name === "brut_to_disponible"} + <StandardOfLiving {situation} {valuesByCalculationNameByVariableName} /> + {/if} + + <!--ZONE 3.A : TICKET DE CARBURANT--> + {#if shared.waterfall.name === "disponible_to_disponible_apres_taxes_carburant"} + {#each oilSpendings as { depenseTtcVariableName, nombreLitresVariableName, prixTtcLitreVariableName, ticpeVariableName, tvaVariableName }} + <OilSpendingBill + {depenseTtcVariableName} + {nombreLitresVariableName} + {prixTtcLitreVariableName} + on:changeTestCaseToEditIndex + {situation} + {situationIndex} + {ticpeVariableName} + {tvaVariableName} + {valuesByCalculationNameByVariableName} + {year} + /> + {/each} + {:else} + <!--ZONE 3.B : GRAPHIQUE DES CAS TYPES--> + {#if firstPersonActivity !== "inactif"} + <button + class="mt-10 flex w-full items-center gap-2 border-b px-4 py-2 text-start text-neutral-600 transition-all hover:bg-neutral-100 active:bg-neutral-200" + onclick={() => { + if (situation.slider === undefined) { + requestAxesCalculation() + trackTestCaseGraph(firstPersonActivity) + } else { + removeSituationSlider() + } + }} + > + <iconify-icon class="text-lg" icon="ri-line-chart-fill"></iconify-icon> + <span class="flex-1 font-bold"> + Voir ce cas type sur un graphique faisant évoluer {#if firstPersonActivity === "actif"} + le salaire + {:else if firstPersonActivity === "retraite"} + la retraite + {:else if firstPersonActivity === "chomeur"} + le chômage + {:else} + le revenu + {/if} + : + </span> + <iconify-icon + class="text-3xl" + icon={situation.slider !== undefined + ? "ri-arrow-drop-down-line" + : "ri-arrow-drop-right-line"} + ></iconify-icon> + </button> + <TestCaseGraph + {displayMode} + {evaluationByName} + evaluationByNameArray={shared.evaluationByNameArray} + {situation} + {situationIndex} + {useRevaluationInsteadOfLaw} + {valuesByCalculationNameByVariableName} + {variableSummaryByName} + vectorLength={shared.vectorLength} + waterfall={shared.waterfall} + {year} + /> + {/if} + {/if} +{/if} + +<style lang="postcss"> + .fond { + background-color: #ffffff; + /* Polka dots - Heropatterns.com échelle réduite */ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3E%3Cpath fill='%23A0A0A0' fill-opacity='0.4' d='M1 3h1v1H1V3zm2-2h1v1H3V1z'%3E%3C/path%3E%3C/svg%3E"); + } +</style> diff --git a/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/LinkedVariablesCompareView.svelte b/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/LinkedVariablesCompareView.svelte new file mode 100644 index 0000000000000000000000000000000000000000..74abac17fe974ee42c5a2c86dd82385a3606c190 --- /dev/null +++ b/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/LinkedVariablesCompareView.svelte @@ -0,0 +1,180 @@ +<script lang="ts"> + import { + type Decomposition, + type EvaluationByName, + } from "$lib/decompositions" + import type { DisplayMode } from "$lib/displays" + import { billName, revaluationName, shared } from "$lib/shared.svelte" + import { variableSummaryByNameByReformName } from "$lib/variables" + import type { + DecompositionReference, + VariableByName, + } from "@openfisca/json-model" + import VariableValueChange from "$lib/components/impacts_view/ValueChange.svelte" + import { + isNullVariableValueByCalculationName, + variableValueByCalculationNameFromEvaluation, + } from "$lib/calculations.svelte" + import { newSimulationUrl } from "$lib/urls" + + interface Props { + displayMode: DisplayMode + evaluationByNameArray: EvaluationByName[] + situationsToCompareIndex: number[] + variableSummaryByName: VariableByName + decomposition: Decomposition + visibleChildren: DecompositionReference[] | undefined + } + + let { + displayMode, + evaluationByNameArray, + situationsToCompareIndex, + variableSummaryByName, + decomposition, + visibleChildren, + }: Props = $props() + + const variableSummary = + billName === undefined + ? variableSummaryByName[displayMode.parametersVariableName!] + : variableSummaryByNameByReformName[billName][ + displayMode.parametersVariableName! + ] + + const linkedVariables = variableSummary.linked_other_variables + const situationIndexLeft = situationsToCompareIndex[0] + const situationIndexRight = situationsToCompareIndex[1] + let evaluationByNameLeft: EvaluationByName = $derived( + evaluationByNameArray[situationIndexLeft], + ) + let evaluationByNameRight: EvaluationByName = $derived( + evaluationByNameArray[situationIndexRight], + ) +</script> + +{#if linkedVariables !== undefined} + <div + class="fond flex w-full grow border-dashed border-black px-4" + class:border-l-0={!decomposition.open} + class:border-l-2={decomposition.open && visibleChildren !== undefined} + class:pl-0.5={!decomposition.open} + > + <!-- <div + class="mb-2 flex w-full grow flex-col justify-end lg:flex-row" + id="situation_{situationIndexLeft}_totalimpact" + > --> + <div class="flex w-full grow justify-around py-3 pl-5"> + {#if linkedVariables !== undefined} + {@const linkedVariablesValueByCalculationNameLeft = linkedVariables.map( + (name) => + variableValueByCalculationNameFromEvaluation( + evaluationByNameLeft[name], + revaluationName, + billName, + shared.parametricReform, + ), + )} + {#if shared.showNulls || !linkedVariablesValueByCalculationNameLeft.every(isNullVariableValueByCalculationName)} + <ul + class="flex h-fit flex-col rounded-md border bg-white p-2 text-gray-800" + > + {#each linkedVariables as linkedVariableName, index} + {@const linkedVariableValueByCalculationNameLeft = + linkedVariablesValueByCalculationNameLeft[index]} + {#if shared.showNulls || !isNullVariableValueByCalculationName(linkedVariableValueByCalculationNameLeft)} + {@const linkedVariableSummary = + billName === undefined + ? variableSummaryByName[linkedVariableName] + : variableSummaryByNameByReformName[billName][ + linkedVariableName + ]} + <li class="flex justify-between gap-2 text-sm"> + <a + class="2xl:text-md xs:max-w-44 max-w-32 cursor-pointer overflow-x-hidden text-ellipsis text-nowrap hover:underline sm:max-w-32 lg:max-w-44 2xl:max-w-none" + href={newSimulationUrl({ + ...displayMode, + parametersVariableName: linkedVariableName, + })} + data-sveltekit-noscroll + ><span class="text-gray-600" + >{linkedVariableSummary.short_label ?? + linkedVariableSummary.label ?? + linkedVariableName} : </span + ></a + > + <VariableValueChange + {evaluationByNameLeft} + name={linkedVariableName} + valueByCalculationName={linkedVariableValueByCalculationNameLeft} + inline + bold + /> + </li> + {/if} + {/each} + </ul> + {/if} + + {@const linkedVariablesValueByCalculationNameRight = + linkedVariables.map((name) => + variableValueByCalculationNameFromEvaluation( + evaluationByNameRight[name], + revaluationName, + billName, + shared.parametricReform, + ), + )} + {#if shared.showNulls || !linkedVariablesValueByCalculationNameRight.every(isNullVariableValueByCalculationName)} + <ul + class="flex h-fit flex-col rounded-md border bg-white p-2 text-gray-800" + > + {#each linkedVariables as linkedVariableName, index} + {@const linkedVariableValueByCalculationNameRight = + linkedVariablesValueByCalculationNameRight[index]} + {#if shared.showNulls || !isNullVariableValueByCalculationName(linkedVariableValueByCalculationNameRight)} + {@const linkedVariableSummary = + billName === undefined + ? variableSummaryByName[linkedVariableName] + : variableSummaryByNameByReformName[billName][ + linkedVariableName + ]} + <li class="flex justify-between gap-2 text-sm"> + <a + class="2xl:text-md xs:max-w-44 max-w-32 cursor-pointer overflow-x-hidden text-ellipsis text-nowrap hover:underline sm:max-w-32 lg:max-w-44 2xl:max-w-none" + href={newSimulationUrl({ + ...displayMode, + parametersVariableName: linkedVariableName, + })} + data-sveltekit-noscroll + ><span class="text-gray-600" + >{linkedVariableSummary.short_label ?? + linkedVariableSummary.label ?? + linkedVariableName} : </span + ></a + > + <VariableValueChange + {evaluationByNameRight} + name={linkedVariableName} + valueByCalculationName={linkedVariableValueByCalculationNameRight} + inline + bold + /> + </li> + {/if} + {/each} + </ul> + {/if} + {/if} + </div> + <!-- </div> --> + </div> +{/if} + +<style lang="postcss"> + .fond { + background-color: #ffffff; + /* Polka dots - Heropatterns.com échelle réduite */ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3E%3Cpath fill='%23A0A0A0' fill-opacity='0.4' d='M1 3h1v1H1V3zm2-2h1v1H3V1z'%3E%3C/path%3E%3C/svg%3E"); + } +</style> diff --git a/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/PaySlipCompareView.svelte b/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/PaySlipCompareView.svelte index 9243424b99151b199b071e9a85e909098d3244df..7e651b9e2368decc7763c202bb60212df3a58264 100644 --- a/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/PaySlipCompareView.svelte +++ b/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/PaySlipCompareView.svelte @@ -1,14 +1,13 @@ <script lang="ts"> - import { preventDefault } from "svelte/legacy" - import type { VariableByName } from "@openfisca/json-model" import { goto } from "$app/navigation" import { page } from "$app/stores" import Tooltip from "$lib/components/ui_transverse_components/Tooltip.svelte" - import type { - EvaluationByName, - VisibleDecompositionForComparison, + import { + type EvaluationByName, + type VisibleDecompositionForComparison, + isChildOrDescendant, } from "$lib/decompositions" import { buildVisibleDecompositionsForComparison } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" @@ -17,6 +16,7 @@ import type { Situation } from "$lib/situations" import { newSimulationUrl } from "$lib/urls" import { removeNegativeZero } from "$lib/values" + import LinkedVariablesCompareView from "$lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/LinkedVariablesCompareView.svelte" interface Props { displayMode: DisplayMode @@ -94,6 +94,21 @@ } } + function getCorrectSimulationUrl(variableName: string) { + const newDisplayMode = + displayMode.edit !== undefined + ? { + ...displayMode, + variableName: variableName, + } + : { + ...displayMode, + mobileLaw: true, + parametersVariableName: variableName, + } + return newSimulationUrl(newDisplayMode) + } + function zoomIn(index: number) { let visibleDecomposition = visibleDecompositions[index] if ( @@ -133,9 +148,9 @@ {#if visibleDecompositions.length > 0} <div> - <div class="h-5 bg-gradient-to-b from-gray-100 to-transparent" /> + <div class="h-5 bg-gradient-to-b from-gray-100 to-transparent"></div> <!--Montants-nuls--> - <div class="mx-1 mb-3 flex justify-start"> + <div class="mx-4 mb-3 flex justify-start"> <label class="inline-flex cursor-pointer items-center"> <input type="checkbox" @@ -152,258 +167,336 @@ </label> </div> </div> - <!-- Labels and compared amounts of decompositions --> - <div> + <!-- Feuille de paie--> + + <div class="flex-auto"> {#each visibleDecompositions as { decomposition, depth, rows, trunk, visibleChildren }, index} - <!-- Decomposition label --> - <div class="flex h-7 items-center whitespace-nowrap"> - {#if trunk} + <!-- Ligne de la feuille de paie--> + <div + class="flex min-h-8 border-t border-gray-200 px-4" + class:border-gray-300={trunk && index !== 0} + class:fond={decomposition.name === displayMode.parametersVariableName || + (displayMode.parametersVariableName && + isChildOrDescendant( + shared.decompositionByName, + decomposition.name, + displayMode.parametersVariableName, + ))} + class:text-lg={decomposition.name === + displayMode.parametersVariableName} + > + <!--Indentation pour chaque niveau de l'arbre, illustré par une bordure--> + {#each iterToDepth(depth)} <div - class="ml-2 mt-2 w-full overflow-x-hidden text-ellipsis border-gray-300 text-left text-base text-gray-500 hover:z-20 hover:overflow-x-visible" - class:border-t={index !== 0} + class={`min-h-full border-l-2 pr-3 ${ + decomposition.name !== displayMode.parametersVariableName && + !( + displayMode.parametersVariableName && + isChildOrDescendant( + shared.decompositionByName, + decomposition.name, + displayMode.parametersVariableName, + ) + ) + ? "border-gray-400" + : "border-black" + }`} > - {#if displayMode.edit !== undefined} - <!-- Trunk decomposition node, in variable inputs mode => link --> - <a - class="cursor-pointer text-base hover:underline" - href={newSimulationUrl({ - ...displayMode, - variableName: decomposition.name, - })} - data-sveltekit-noscroll - >{decomposition.short_label ?? decomposition.label}</a - > - {:else} - <!-- Trunk decomposition node with parameters, in parameters mode => link --> - <a - class="cursor-pointer text-base hover:underline" - href={newSimulationUrl({ - ...displayMode, - mobileLaw: true, - parametersVariableName: decomposition.name, - })} - data-sveltekit-noscroll - >{decomposition.short_label ?? decomposition.label}</a - > - {/if} + </div> - {:else} - {#each [...iterToDepth(depth)] as _level} - <div class="ml-2 h-full border-l border-le-gris-dispositif"> - - </div> - {/each} - {#if visibleChildren === undefined} - <!-- Leaf node (except the first one, that belongs to trunk) --> - <div class="ml-4"> - {#if decomposition.obsolete || decomposition.last_value_still_valid_on === undefined || decomposition.last_value_still_valid_on < (new Date().getFullYear() - 2).toString()} - <Tooltip - allowFlip={false} - arrowClass="bg-gray-100" - widthClass="w-80" - initialPlacement="bottom" - > - <iconify-icon - class="mr-1 shadow-none {decomposition.obsolete - ? 'text-[#FF4133]' - : 'text-[#FFAC33]'}" - icon="material-symbols:warning-rounded" - width="16" - height="16" - ></iconify-icon> - {#snippet tooltip()} + {/each} + <div class="flex w-full flex-col"> + <!-- "Nom du dispositif" --> + <div class="flex-1 overflow-x-hidden"> + {#each rows as { calculationName }} + {#if calculationName === firstCalculationName} + <div class="flex h-full w-full items-center"> + {#if trunk && index !== 0} + <!--Les class permettent que le nom de la variable soit caché avec "..." et rendu visible au survol. Le comportement est différent si la variable est sélectionnée--> <div - class="overflow-hidden rounded-lg border border-gray-200 bg-white shadow-2xl" + class="flex w-full items-center" + class:whitespace-nowrap={decomposition.name !== + displayMode.parametersVariableName} > - <div - class="border-b border-gray-200 bg-gray-100 px-3 py-2" + <!-- Nom de la variable tronc, cliquable --> + <a + class="cursor-pointer overflow-x-hidden text-ellipsis text-gray-500 hover:z-20 hover:overflow-x-visible hover:bg-white hover:bg-opacity-90 hover:pr-1 hover:text-le-gris-dispositif-dark hover:underline" + class:font-bold={decomposition.name === + displayMode.parametersVariableName} + href={getCorrectSimulationUrl(decomposition.name)} + data-sveltekit-noscroll + >{decomposition.short_label ?? decomposition.label}</a > - <h3 class="font-semibold text-gray-900"> - âš ï¸ Ce dispositif n'est peut-être pas à jour - </h3> - </div> - <div class="px-3 py-2 text-sm font-light text-gray-500"> - Dernière relecture : - {#if decomposition.last_value_still_valid_on === undefined} - date indéterminée - {:else}{dateFormatter( - new Date(decomposition.last_value_still_valid_on), - )} - {/if} - {#if decomposition.obsolete} - ; Obsolète !{/if} - </div> </div> - {/snippet} - </Tooltip> - {/if} - </div> - - {#if displayMode.edit !== undefined} - <!-- Leaf decomposition node in variable inputs mode => link --> - <a - 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" - href={newSimulationUrl({ - ...displayMode, - variableName: decomposition.name, - })} - data-sveltekit-noscroll - >{decomposition.short_label ?? decomposition.label}</a - > - {:else} - <!-- Leaf decomposition node with parameters in parameters mode => link --> - <a - 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" - href={newSimulationUrl({ - ...displayMode, - mobileLaw: true, - parametersVariableName: decomposition.name, - })} - data-sveltekit-noscroll - >{decomposition.short_label ?? decomposition.label}</a - > - {/if} - {:else} - <!-- Non-trunk, non-leaf variablev--> - {#if decomposition.open} - <button class="p-0 text-black" onclick={() => zoomOut(index)}> - <iconify-icon - class="align-[-0.2rem] text-lg" - icon="ri-arrow-up-s-line" - ></iconify-icon> - </button> - {:else} - <button class="p-0 text-black" onclick={() => zoomIn(index)}> - <iconify-icon - class="align-[-0.2rem] text-lg" - icon="ri-arrow-right-s-line" - ></iconify-icon> - </button> - {/if} - - {#if displayMode.edit !== undefined} - <!-- Non-lead decomposition node in variable inputs mode => no-link --> - <button - class="cursor-pointer overflow-x-hidden text-ellipsis font-serif text-base text-black hover:z-20 hover:overflow-x-visible hover:bg-white hover:text-black hover:underline" - onclick={() => { - if (decomposition.open) { - zoomOut(index) - } else { - zoomIn(index) - } - }} - > - {decomposition.short_label ?? decomposition.label} - </button> - {:else} - <!-- Leaf decomposition node with parameters in parameters mode => link --> - <a - 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" - href={newSimulationUrl({ - ...displayMode, - parametersVariableName: decomposition.name, - })} - onclick={preventDefault(() => { - if (decomposition.open) { - zoomOut(index) - } else { - zoomIn(index) - } - goto( - newSimulationUrl({ - ...displayMode, - parametersVariableName: decomposition.name, - }), - { noScroll: true }, - ) - })} - data-sveltekit-noscroll - >{decomposition.short_label ?? decomposition.label}</a - > - {/if} - {/if} - {/if} - </div> + {:else if visibleChildren === undefined} + <!--Si c'est une variable Non-trunk, leaf-variable : La variable n'a pas de variables enfants et elle n'est pas une variable tronc --> - {#each rows as { calculationName, deltaAtVectorIndexArray }} - <!-- Decomposition compared amounts --> - {#if !decomposition.open || trunk || index === 0} - <div class="relative flex items-center whitespace-nowrap"> - {#if !decomposition.open && !trunk} - <div class="absolute"> - {#each iterToDepth(depth)} - <div class="ml-2 h-full border-l border-le-gris-dispositif"> - - </div> - {/each} - </div> - {/if} - <div - class="justify-center {decomposition.open && - trunk && - visibleChildren.length > 1 && - calculationName === firstCalculationName - ? '' - : ' border-none'} {calculationName === firstCalculationName - ? '' - : '-mt-1 mb-1'} flex w-full text-sm" - > - {#if decomposition.open || index === 0} - <!---Composant loader pour les valeurs intermédiaires--> - {#if showLoader && runningCalculationNames.length > 0} - {#if runningCalculationNames.includes("law") || runningCalculationNames.includes("revaluation")}<span - class="mx-1 animate-pulse-2 bg-gray-500 px-1 text-black blur-xs" - > - <span class="text-white blur">value €</span> - </span> - {/if} - {#if runningCalculationNames.includes("bill")} - <span - class="mx-1 animate-pulse-2 bg-le-rouge-bill px-1 text-black blur-xs" + <div + class="flex h-full w-full items-center" + class:whitespace-nowrap={decomposition.name !== + displayMode.parametersVariableName} > - <span class="text-white blur">value €</span> - </span> - {/if} - {#if runningCalculationNames.includes("amendment") && modificationsAmendmentCount > 0} - <span - class="mx-1 animate-pulse-2 bg-le-jaune px-1 text-black blur-xs" + <!--Si la variable est obsolète, ou que sa date de relecture est inconnue ou trop ancienne, un picto attention est affiché --> + {#if decomposition.obsolete || decomposition.last_value_still_valid_on === undefined || decomposition.last_value_still_valid_on < (new Date().getFullYear() - 2).toString()} + <Tooltip + allowFlip={false} + arrowClass="bg-gray-100" + widthClass="w-80" + initialPlacement="bottom" + > + <iconify-icon + class="mr-0.5 shadow-none {decomposition.obsolete + ? 'text-[#FF4133]' + : 'text-[#FFAC33]'}" + icon="material-symbols:warning-rounded" + width="16" + height="16" + ></iconify-icon> + {#snippet tooltip()} + <div + class="overflow-hidden rounded-lg border border-gray-200 bg-white shadow-2xl" + > + <div + class="border-b border-gray-200 bg-gray-100 px-3 py-2" + > + <h3 class="font-semibold text-gray-900"> + âš ï¸ Ce dispositif n'est peut-être pas à jour + </h3> + </div> + <div + class="px-3 py-2 text-sm font-light text-gray-500" + > + Dernière relecture : + {#if decomposition.last_value_still_valid_on === undefined} + date indéterminée + {:else}{dateFormatter( + new Date( + decomposition.last_value_still_valid_on, + ), + )} + {/if} + {#if decomposition.obsolete} + ; Obsolète !{/if} + </div> + </div> + {/snippet} + </Tooltip> + {/if} + <!-- Nom de la variable non-trunk, leaf, cliquable --> + <a + class="cursor-pointer overflow-x-hidden text-ellipsis font-serif hover:z-20 hover:overflow-x-visible hover:bg-white hover:bg-opacity-90 hover:pr-1 hover:text-le-gris-dispositif-dark hover:underline" + class:font-bold={decomposition.name === + displayMode.parametersVariableName} + class:hover:text-wrap={decomposition.name !== + displayMode.parametersVariableName} + href={getCorrectSimulationUrl(decomposition.name)} + data-sveltekit-noscroll + >{decomposition.short_label ?? decomposition.label}</a + > + </div> + {:else} + <!-- Si c'est une variable Non-trunk, Non-leaf : La variable a des variables enfants et n'est pas une variable tronc | une flèche à droite permet d'ouvrir les variables enfants--> + + <div + class="flex h-full w-full items-center justify-start" + class:whitespace-nowrap={decomposition.name !== + displayMode.parametersVariableName} > - <span class="text-white blur">value €</span> - </span> + <button + class="cursor-pointer overflow-x-hidden text-ellipsis text-left font-serif hover:z-20 hover:overflow-x-visible hover:bg-white hover:bg-opacity-90 hover:text-le-gris-dispositif-dark hover:underline" + class:hover:text-wrap={decomposition.name !== + displayMode.parametersVariableName} + class:font-bold={decomposition.name === + displayMode.parametersVariableName} + onclick={() => { + // Non-leaf decomposition node in variable inputs mode => no-link + if (decomposition.open) { + zoomOut(index) + } else { + zoomIn(index) + } + // Leaf decomposition node with parameters in parameters mode => link + if (displayMode.edit === undefined) { + goto(getCorrectSimulationUrl(decomposition.name), { + noScroll: true, + }) + } + }} + data-sveltekit-noscroll + >{decomposition.short_label ?? + decomposition.label}</button + > + <button + class="text-black" + aria-label={decomposition.open + ? "Ouvrir variables enfants" + : "Fermer"} + onclick={() => + decomposition.open ? zoomOut(index) : zoomIn(index)} + > + <iconify-icon + class="align-[-0.25rem] text-lg hover:text-le-gris-dispositif" + icon={decomposition.open + ? "ri-arrow-down-s-line" + : "ri-arrow-right-s-line"} + ></iconify-icon> + </button> + </div> {/if} - <!--Valeurs du waterfall des montants intermédiaires --> - {:else if trunk || index === 0} - {#if deltaAtVectorIndexArray[0] === deltaAtVectorIndexArray[1]} - {#if decomposition.open && trunk} - <span class="mx-2 text-gray-500">=</span> + </div> + {/if} + {/each} + </div> + + <!-- "Valeur du dispositif"--> + + <div class="flex w-full flex-col items-stretch"> + {#each rows as { calculationName, deltaAtVectorIndexArray }} + <div class="h-full justify-center pl-1 text-sm"> + <!--Si la valeur charge, alors un squelette est affiché --> + {#if showLoader && runningCalculationNames.length > 0} + <div class="flex w-full justify-center"> + {#each iterToDepth(depth)} + <div class="-mr-3"></div> + {/each} + {#if runningCalculationNames.includes("law") || runningCalculationNames.includes("revaluation")} + <span + class="animate-pulse-2 bg-gray-500 px-1 text-black blur-xs" + > + <span class="text-white blur">value €</span> + </span> + {/if} + + {#if runningCalculationNames.includes("bill")} + {#each iterToDepth(depth)} + <div class="-mr-3"></div> + {/each} + <span + class="animate-pulse-2 bg-le-rouge-bill px-1 text-black blur-xs" + > + <span class="text-white blur">value €</span> + </span> + {/if} + {#if runningCalculationNames.includes("amendment") && modificationsAmendmentCount > 0} + <span + class="animate-pulse-2 bg-le-jaune px-1 text-black blur-xs" + > + <span class="text-white blur">value €</span> + </span> {/if} - <span - class="text-sm {calculationName === firstCalculationName - ? rows.find((row) => row.calculationName === 'bill') === - undefined + </div> + <!--Si la valeur du cas type 0 est égale à la valeur du cas type 1 : Une seule valeur est alors affichée --> + {:else if deltaAtVectorIndexArray[0] === deltaAtVectorIndexArray[1]} + <div class="flex w-full justify-center"> + <!--Retrait de la marge appliquée avec l'identation--> + {#each iterToDepth(depth)} + <div class="-mr-3"></div> + {/each} + {#if trunk && index !== 0} + <span + class={calculationName === firstCalculationName ? rows.find( - (row) => row.calculationName === 'amendment', + (row) => row.calculationName === "bill", ) === undefined - ? 'text-gray-500' - : 'text-gray-500 line-through-amendment' - : 'text-gray-500 line-through-bill' - : calculationName === 'bill' + ? rows.find( + (row) => row.calculationName === "amendment", + ) === undefined + ? "text-gray-500" + : "text-gray-500 line-through-amendment" + : "text-gray-500 line-through-bill" + : calculationName === "bill" + ? rows.find( + (row) => row.calculationName === "amendment", + ) === undefined + ? "text-le-rouge-bill" + : "text-le-rouge-bill line-through-amendment" + : "bg-le-jaune text-gray-500"} + class:text-base={decomposition.name !== + displayMode.parametersVariableName} + class:text-2xl={decomposition.name === + displayMode.parametersVariableName} + class:font-bold={decomposition.name === + displayMode.parametersVariableName} + ><span + class="content-center pr-1 font-normal text-gray-500" + >=</span + >{deltaFormatter(deltaAtVectorIndexArray[0] ?? 0)}</span + > + {:else} + <span + class="font-bold {calculationName === + firstCalculationName ? rows.find( - (row) => row.calculationName === 'amendment', + (row) => row.calculationName === 'bill', ) === undefined - ? 'text-le-rouge-bill' - : 'text-le-rouge-bill line-through-amendment' - : 'bg-le-jaune'}" - >{deltaFormatter(deltaAtVectorIndexArray[0] ?? 0)}</span - > - {:else} - <div class="flex w-full justify-center"> - <div class="mx-4 w-1/2"> - {#if decomposition.open && trunk} - <span class="mx-2 text-gray-500">=</span> - {/if} + ? rows.find( + (row) => row.calculationName === 'amendment', + ) === undefined + ? '' + : 'line-through-amendment' + : 'line-through-bill' + : calculationName === 'bill' + ? rows.find( + (row) => row.calculationName === 'amendment', + ) === undefined + ? 'text-le-rouge-bill' + : 'text-le-rouge-bill line-through-amendment' + : 'bg-le-jaune'}" + class:opacity-20={decomposition.open} + class:text-base={decomposition.name !== + displayMode.parametersVariableName} + class:text-2xl={decomposition.name === + displayMode.parametersVariableName} + class:font-semibold={decomposition.name === + displayMode.parametersVariableName} + >{#if index !== 0}{#if deltaAtVectorIndexArray[0] < 0}-{:else if deltaAtVectorIndexArray[0] > 0}+{/if}{/if} + {deltaFormatter(deltaAtVectorIndexArray[0] ?? 0)}</span + > + {/if} + </div> + <!--Si la valeur du cas type 0 est DIFFÉRENTE de la valeur du cas type 1 : les deux valeurs sont affichées côte à côte --> + {:else} + <div class="flex w-full text-center"> + <!--Retrait de la marge appliquée avec l'identation--> + {#each iterToDepth(depth)} + <div class="-mr-3"></div> + {/each} + <div class="mx-4 w-1/2"> + {#if trunk && index !== 0} + <span + class={calculationName === firstCalculationName + ? rows.find( + (row) => row.calculationName === "bill", + ) === undefined + ? rows.find( + (row) => row.calculationName === "amendment", + ) === undefined + ? "text-gray-500" + : "text-gray-500 line-through-amendment" + : "text-gray-500 line-through-bill" + : calculationName === "bill" + ? rows.find( + (row) => row.calculationName === "amendment", + ) === undefined + ? "text-le-rouge-bill" + : "text-le-rouge-bill line-through-amendment" + : "bg-le-jaune text-gray-500"} + class:text-base={decomposition.name !== + displayMode.parametersVariableName} + class:text-2xl={decomposition.name === + displayMode.parametersVariableName} + class:font-bold={decomposition.name === + displayMode.parametersVariableName} + ><span + class="content-center pr-1 font-normal text-gray-500" + >=</span + >{deltaFormatter( + deltaAtVectorIndexArray[0] ?? 0, + )}</span + > + {:else} <span - class="text-right text-sm {calculationName === + class="text-base font-bold {calculationName === firstCalculationName ? rows.find( (row) => row.calculationName === 'bill', @@ -411,9 +504,9 @@ ? rows.find( (row) => row.calculationName === 'amendment', ) === undefined - ? 'text-gray-500' - : 'text-gray-500 line-through-amendment' - : 'text-gray-500 line-through-bill' + ? '' + : 'line-through-amendment' + : 'line-through-bill' : calculationName === 'bill' ? rows.find( (row) => row.calculationName === 'amendment', @@ -421,17 +514,49 @@ ? 'text-le-rouge-bill' : 'text-le-rouge-bill line-through-amendment' : 'bg-le-jaune'}" - >{deltaFormatter( + class:opacity-20={decomposition.open} + >{#if trunk && index === 0}={:else if deltaAtVectorIndexArray[0] < 0}-{:else if deltaAtVectorIndexArray[0] > 0}+{/if}{deltaFormatter( deltaAtVectorIndexArray[0] ?? 0, )}</span > - </div> - <div class="mx-4 w-1/2"> - {#if decomposition.open && trunk} - <span class="mx-2 text-gray-500">=</span> - {/if} + {/if} + </div> + <div class="mx-4 w-1/2"> + {#if trunk && index !== 0} + <span + class={calculationName === firstCalculationName + ? rows.find( + (row) => row.calculationName === "bill", + ) === undefined + ? rows.find( + (row) => row.calculationName === "amendment", + ) === undefined + ? "text-gray-500" + : "text-gray-500 line-through-amendment" + : "text-gray-500 line-through-bill" + : calculationName === "bill" + ? rows.find( + (row) => row.calculationName === "amendment", + ) === undefined + ? "text-le-rouge-bill" + : "text-le-rouge-bill line-through-amendment" + : "bg-le-jaune text-gray-500"} + class:text-base={decomposition.name !== + displayMode.parametersVariableName} + class:text-2xl={decomposition.name === + displayMode.parametersVariableName} + class:font-bold={decomposition.name === + displayMode.parametersVariableName} + ><span + class="content-center pr-1 font-normal text-gray-500" + >=</span + >{deltaFormatter( + deltaAtVectorIndexArray[1] ?? 0, + )}</span + > + {:else} <span - class="text-left text-sm {calculationName === + class="text-base font-bold {calculationName === firstCalculationName ? rows.find( (row) => row.calculationName === 'bill', @@ -439,9 +564,9 @@ ? rows.find( (row) => row.calculationName === 'amendment', ) === undefined - ? 'text-gray-500' - : 'text-gray-500 line-through-amendment' - : 'text-gray-500 line-through-bill' + ? '' + : 'line-through-amendment' + : 'line-through-bill' : calculationName === 'bill' ? rows.find( (row) => row.calculationName === 'amendment', @@ -449,140 +574,39 @@ ? 'text-le-rouge-bill' : 'text-le-rouge-bill line-through-amendment' : 'bg-le-jaune'}" - >{deltaFormatter( + class:opacity-20={decomposition.open} + >{#if trunk && index === 0}={:else if deltaAtVectorIndexArray[1] < 0}-{:else if deltaAtVectorIndexArray[1] > 0}+{/if}{deltaFormatter( deltaAtVectorIndexArray[1] ?? 0, )}</span > - </div> - </div> - {/if} - {/if} - <!--Montant des dispositifs--> - {:else if deltaAtVectorIndexArray[0] === deltaAtVectorIndexArray[1]} - <!---Composant loader pour la valeur suivante--> - {#if showLoader && runningCalculationNames.length > 0} - {#if runningCalculationNames.includes("law") || runningCalculationNames.includes("revaluation")}<span - class="mx-1 animate-pulse-2 bg-gray-500 px-1 text-black blur-xs" - > - <span class="text-white blur">value €</span> - </span> - {/if} - {#if runningCalculationNames.includes("bill")} - <span - class="mx-1 animate-pulse-2 bg-le-rouge-bill px-1 text-black blur-xs" - > - <span class="text-white blur">value €</span> - </span> - {/if} - {#if runningCalculationNames.includes("amendment") && modificationsAmendmentCount > 0} - <span - class="mx-1 animate-pulse-2 bg-le-jaune px-1 text-black blur-xs" - > - <span class="text-white blur">value €</span> - </span> - {/if} - <!--Valeur--> - {:else} - <span - class="text-base font-bold {calculationName === - firstCalculationName - ? rows.find((row) => row.calculationName === 'bill') === - undefined - ? rows.find( - (row) => row.calculationName === 'amendment', - ) === undefined - ? '' - : 'line-through-amendment' - : 'line-through-bill' - : calculationName === 'bill' - ? rows.find( - (row) => row.calculationName === 'amendment', - ) === undefined - ? 'text-le-rouge-bill' - : 'text-le-rouge-bill line-through-amendment' - : 'bg-le-jaune'}" - >{deltaFormatter(deltaAtVectorIndexArray[0] ?? 0)}</span - > - {/if} - {:else} - <!---Composant loader pour la valeur suivante--> - {#if showLoader && runningCalculationNames.length > 0} - {#if runningCalculationNames.includes("law") || runningCalculationNames.includes("revaluation")}<span - class="mx-1 animate-pulse-2 bg-gray-500 px-1 text-black blur-xs" - > - <span class="text-white blur">value €</span> - </span> - {/if} - {#if runningCalculationNames.includes("bill")} - <span - class="mx-1 animate-pulse-2 bg-le-rouge-bill px-1 text-black blur-xs" - > - <span class="text-white blur">value €</span> - </span> - {/if} - {#if runningCalculationNames.includes("amendment") && modificationsAmendmentCount > 0} - <span - class="mx-1 animate-pulse-2 bg-le-jaune px-1 text-black blur-xs" - > - <span class="text-white blur">value €</span> - </span> - {/if} - <!--Valeur dispositifs--> - {:else} - <div class="flex w-full justify-center"> - <div class="mx-4 w-1/2"> - <span - class="text-base font-bold {calculationName === - firstCalculationName - ? rows.find( - (row) => row.calculationName === 'bill', - ) === undefined - ? rows.find( - (row) => row.calculationName === 'amendment', - ) === undefined - ? '' - : 'line-through-amendment' - : 'line-through-bill' - : calculationName === 'bill' - ? rows.find( - (row) => row.calculationName === 'amendment', - ) === undefined - ? 'text-le-rouge-bill' - : 'text-le-rouge-bill line-through-amendment' - : 'bg-le-jaune'}" - >{deltaFormatter(deltaAtVectorIndexArray[0] ?? 0)}</span - > - </div> - <div class="mx-4 w-1/2"> - <span - class="text-base font-bold {calculationName === - firstCalculationName - ? rows.find( - (row) => row.calculationName === 'bill', - ) === undefined - ? rows.find( - (row) => row.calculationName === 'amendment', - ) === undefined - ? '' - : 'line-through-amendment' - : 'line-through-bill' - : calculationName === 'bill' - ? rows.find( - (row) => row.calculationName === 'amendment', - ) === undefined - ? 'text-le-rouge-bill' - : 'text-le-rouge-bill line-through-amendment' - : 'bg-le-jaune'}" - >{deltaFormatter(deltaAtVectorIndexArray[1] ?? 0)}</span - > + {/if} </div> </div> {/if} - {/if} - </div> + </div> + {/each} + <!--Affichage des variables liées s'il y en a--> + {#if displayMode.parametersVariableName !== undefined && decomposition.name === displayMode.parametersVariableName} + <LinkedVariablesCompareView + {displayMode} + {evaluationByNameArray} + {situationsToCompareIndex} + {variableSummaryByName} + {decomposition} + {visibleChildren} + /> + {/if} </div> - {/if} - {/each} + </div> + </div> {/each} </div> {/if} + +<style lang="postcss"> + .fond { + background-color: #ffffff; + /* Polka dots - Heropatterns.com échelle réduite */ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3E%3Cpath fill='%23A0A0A0' fill-opacity='0.4' d='M1 3h1v1H1V3zm2-2h1v1H3V1z'%3E%3C/path%3E%3C/svg%3E"); + } +</style> diff --git a/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/TestCaseCompareView.svelte b/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/TestCaseCompareView.svelte index 2f884fbafa95949349fb8af697ab0ccdd54b39d1..b5c7b433d141db07b4b2cce087b506c685eb74a0 100644 --- a/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/TestCaseCompareView.svelte +++ b/src/lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/TestCaseCompareView.svelte @@ -1,14 +1,14 @@ <script lang="ts"> import type { DecompositionReference } from "@openfisca/json-model" + import SourcesMethodTooltip from "$lib/components/impacts_view/SourcesMethodTooltip.svelte" import { createEventDispatcher } from "svelte" import type { CalculationName } from "$lib/calculations.svelte" import TestCaseSummary from "$lib/components/impacts_view/test_cases_view/test_case_selected/TestCaseSummary.svelte" - import ValueChangeCompare from "$lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/ValueChangeCompare.svelte" import PaySlipCompareView from "$lib/components/impacts_view/test_cases_view/test_case_selected/compare_mode/PaySlipCompareView.svelte" import { type EvaluationByName, waterfalls } from "$lib/decompositions" import type { DisplayMode } from "$lib/displays" - import { billName, revaluationName, shared } from "$lib/shared.svelte" + import { shared } from "$lib/shared.svelte" import { newSimulationUrl } from "$lib/urls" import { variableSummaryByName } from "$lib/variables" @@ -100,217 +100,9 @@ </div> <div id="situation_{situationsToCompareIndex.join('_')}_waterfall"> {#if variableSummaryByName !== undefined} - <div class="flex justify-between bg-gray-100"> + <div class="flex w-full justify-between bg-gray-200"> <!-- Impacts et waterfall --> - <div - class="min-h-[30em] w-10/12 bg-gradient-to-r from-gray-100 from-50% to-white to-50% bg-cover bg-center bg-no-repeat px-3 text-center md:w-11/12" - > - <div class="relative flex-col border-gray-400 p-3 pb-6"> - <div - class="flex justify-between gap-4" - title="âš ï¸ Les dispositifs n'étant pas tous à jour, cette somme est à considérer avec prudence." - > - <div class="mb-4 w-full flex-col text-gray-700"> - <p class="mb-1 text-base font-medium"> - {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" - xmlns="http://www.w3.org/2000/svg" - > - <path - class="text-[#FFAC33]" - d="M0.124322 18.4377C-0.240619 19.1041 0.241623 19.918 1.00142 19.918H20.6259C21.3857 19.918 21.868 19.1041 21.503 18.4377L11.6908 0.519686C11.3113 -0.173228 10.316 -0.173229 9.93658 0.519685L0.124322 18.4377ZM11.8591 16.8375C11.8591 17.3898 11.4114 17.8375 10.8591 17.8375H10.7682C10.2159 17.8375 9.76822 17.3898 9.76822 16.8375V16.627C9.76822 16.0747 10.2159 15.627 10.7682 15.627H10.8591C11.4114 15.627 11.8591 16.0747 11.8591 16.627V16.8375ZM11.8591 12.7416C11.8591 13.2938 11.4114 13.7416 10.8591 13.7416H10.7682C10.2159 13.7416 9.76822 13.2938 9.76822 12.7416V7.3298C9.76822 6.77751 10.2159 6.3298 10.7682 6.3298H10.8591C11.4114 6.3298 11.8591 6.77751 11.8591 7.3298V12.7416Z" - /> - <path - d="M10.7686 17.8378H10.8595C11.4117 17.8378 11.8595 17.3901 11.8595 16.8378V16.6273C11.8595 16.075 11.4117 15.6273 10.8595 15.6273H10.7686C10.2163 15.6273 9.76855 16.075 9.76855 16.6273V16.8378C9.76855 17.3901 10.2163 17.8378 10.7686 17.8378Z" - /> - <path - d="M10.7686 13.7418H10.8595C11.4117 13.7418 11.8595 13.2941 11.8595 12.7418V7.33008C11.8595 6.77779 11.4117 6.33008 10.8595 6.33008H10.7686C10.2163 6.33008 9.76855 6.77779 9.76855 7.33008V12.7418C9.76855 13.2941 10.2163 13.7418 10.7686 13.7418Z" - /> - </svg>: - </p> - <ValueChangeCompare - legend - unitName="currency-EUR" - valueByCalculationName0={{ - amendment: - Object.keys(shared.parametricReform).length === 0 - ? undefined - : calculateTotal( - shared.evaluationByNameArray[ - situationsToCompareIndex[0] - ], - "amendment", - shared.waterfall.root, - shared.waterfall.total, - ), - bill: - billName === undefined - ? undefined - : calculateTotal( - shared.evaluationByNameArray[ - situationsToCompareIndex[0] - ], - "bill", - shared.waterfall.root, - shared.waterfall.total, - ), - law: calculateTotal( - shared.evaluationByNameArray[situationsToCompareIndex[0]], - "law", - shared.waterfall.root, - shared.waterfall.total, - ), - revaluation: - revaluationName === undefined - ? undefined - : calculateTotal( - shared.evaluationByNameArray[ - situationsToCompareIndex[0] - ], - "revaluation", - shared.waterfall.root, - shared.waterfall.total, - ), - }} - valueByCalculationName1={{ - amendment: - Object.keys(shared.parametricReform).length === 0 - ? undefined - : calculateTotal( - shared.evaluationByNameArray[ - situationsToCompareIndex[1] - ], - "amendment", - shared.waterfall.root, - shared.waterfall.total, - ), - bill: - billName === undefined - ? undefined - : calculateTotal( - shared.evaluationByNameArray[ - situationsToCompareIndex[1] - ], - "bill", - shared.waterfall.root, - shared.waterfall.total, - ), - law: calculateTotal( - shared.evaluationByNameArray[situationsToCompareIndex[1]], - "law", - shared.waterfall.root, - shared.waterfall.total, - ), - revaluation: - revaluationName === undefined - ? undefined - : calculateTotal( - shared.evaluationByNameArray[ - situationsToCompareIndex[1] - ], - "revaluation", - shared.waterfall.root, - shared.waterfall.total, - ), - }} - /> - </div> - <!-- <div class="flex-col w-1/2 text-gray-500 text-sm mt-8"> - <p> - dont <span class="font-bold text-black">100 €</span> de compléments - de ressources. - </p> - <p> - dont <span class="font-bold text-black">200 €</span> de prélèvements - obligatoires. - </p> - </div> --> - </div> - {#if displayMode.parametersVariableName !== undefined && shared.decompositionByName[displayMode.parametersVariableName] !== undefined} - <div class="mt-2 flex-col"> - <p class="mb-1 text-lg font-black"> - {shared.decompositionByName[ - displayMode.parametersVariableName - ].short_label ?? - shared.decompositionByName[ - displayMode.parametersVariableName - ].label ?? - displayMode.parametersVariableName} : - </p> - <div class="text-2xl font-semibold"> - <ValueChangeCompare - unitName="currency-EUR" - valueByCalculationName0={{ - amendment: - Object.keys(shared.parametricReform).length === 0 - ? undefined - : (shared.evaluationByNameArray[ - situationsToCompareIndex[0] - ][displayMode.parametersVariableName] - ?.calculationEvaluationByName["amendment"] - ?.deltaAtVectorIndex ?? 0), - bill: - billName === undefined - ? undefined - : (shared.evaluationByNameArray[ - situationsToCompareIndex[0] - ][displayMode.parametersVariableName] - ?.calculationEvaluationByName["bill"] - ?.deltaAtVectorIndex ?? 0), - law: - shared.evaluationByNameArray[ - situationsToCompareIndex[0] - ][displayMode.parametersVariableName] - ?.calculationEvaluationByName["law"] - ?.deltaAtVectorIndex ?? 0, - revaluation: - revaluationName === undefined - ? undefined - : (shared.evaluationByNameArray[ - situationsToCompareIndex[0] - ][displayMode.parametersVariableName] - ?.calculationEvaluationByName["revaluation"] - ?.deltaAtVectorIndex ?? 0), - }} - valueByCalculationName1={{ - amendment: - Object.keys(shared.parametricReform).length === 0 - ? undefined - : (shared.evaluationByNameArray[ - situationsToCompareIndex[1] - ][displayMode.parametersVariableName] - ?.calculationEvaluationByName["amendment"] - ?.deltaAtVectorIndex ?? 0), - bill: - billName === undefined - ? undefined - : (shared.evaluationByNameArray[ - situationsToCompareIndex[1] - ][displayMode.parametersVariableName] - ?.calculationEvaluationByName["bill"] - ?.deltaAtVectorIndex ?? 0), - law: - shared.evaluationByNameArray[ - situationsToCompareIndex[1] - ][displayMode.parametersVariableName] - ?.calculationEvaluationByName["law"] - ?.deltaAtVectorIndex ?? 0, - revaluation: - revaluationName === undefined - ? undefined - : (shared.evaluationByNameArray[ - situationsToCompareIndex[1] - ][displayMode.parametersVariableName] - ?.calculationEvaluationByName["revaluation"] - ?.deltaAtVectorIndex ?? 0), - }} - /> - </div> - </div> - {/if} - </div> + <div class="relative w-10/12 bg-white pb-4 md:w-11/12"> <PaySlipCompareView {displayMode} evaluationByNameArray={shared.evaluationByNameArray} @@ -321,15 +113,16 @@ /> </div> <!--Onglets--> - <div - class="flex h-[24em] w-2/12 flex-col justify-between bg-gray-100 md:w-1/12" - > + <div class="flex w-2/12 flex-col justify-start bg-gray-200 md:w-1/12"> {#each waterfalls as { icon, label, name }} <a - class="flex grow items-center justify-center shadow-inner" + class="group flex h-[9em] shrink items-center justify-center shadow-inner" class:bg-white={name === shared.waterfall.name} + class:hover:bg-gray-300={name !== shared.waterfall.name} class:border-le-bleu={name === shared.waterfall.name} class:border-r-4={name === shared.waterfall.name} + class:border-l={name !== shared.waterfall.name} + class:border-gray-300={name !== shared.waterfall.name} class:shadow-none={name === shared.waterfall.name} href={newSimulationUrl({ ...displayMode, @@ -338,17 +131,19 @@ data-sveltekit-noscroll > <div class="origin-center rotate-90"> - <div class="mr-5 flex"> + <div class="mr-6 flex"> {#if icon !== undefined} <img class="block origin-center -rotate-90" - alt="" + alt="Icone pour {label}" src={icon} /> {/if} <span - class="ml-2 text-xs uppercase tracking-wide text-gray-500 xl:text-sm" + class="ml-2 text-xs uppercase tracking-wide text-gray-600 xl:text-sm" class:text-le-bleu={name === shared.waterfall.name} + class:group-hover:text-black={name !== + shared.waterfall.name} class:font-bold={name === shared.waterfall.name} > {label} @@ -359,6 +154,7 @@ {/each} </div> </div> + <SourcesMethodTooltip {displayMode} /> {/if} </div> </div>