Skip to content

Commit

Permalink
feat: Download comparison data (#139)
Browse files Browse the repository at this point in the history
Resolves #138

## Contexte

Afin de faire de pouvoir manipuler les données de comparaison des
différentes études, on propose une possibilité de Télécharger le tableau
de comparaison en XLSX

On ajoute un lien en bas du tableau pour ça.

![image](https://github.com/user-attachments/assets/a529324e-e570-46e3-b53d-2cf2660e7a56)

## 👀 Spec choisie en fonction des possibilités de la lib et des
`xlsx`

- Les labels de sous rangs sont affichés avec `----` devant, pour
simuler l'effet "dropdown". En effet, seule la version pro de xlsx
permet de formater du texte. Sinon j'uarais choisi de mettre les
cellules en gras pour les rangs principaux

## Changements

- **Très long refactor**
- Pour cette fonctionalité, j'ai besoin de regrouper la "config" de la
comparaison (ordre des indicateurs, formats, titres) au meme endroit
(pour la factoriser pour l'export)
- J'ai donc extrait une grosse config de comparaison avec titres /
sous-titres / getValue, etc.
- Ensuite j'ai pu supprimer `ComparisonEconomics`, `ComparisonSocial`,
`ComparisonEnvironment` pour une composant générique qui se base sur
cette config.
- Fonctionnalité
- Création d'une fonction qui recrée les rows de la page de comparaison
  - Écriture d'une feuille xlsx a partir de ce tableau
- Spécification des formats de cellules (pour afficher les pourcentage
en %, les score sociaux sans décimales, les valeurs monétaires en USD,
etc.)
  • Loading branch information
maximepiard authored Nov 8, 2024
1 parent c48fa1c commit afa0268
Show file tree
Hide file tree
Showing 15 changed files with 697 additions and 534 deletions.
112 changes: 75 additions & 37 deletions src/components/StudiesComparison.vue
Original file line number Diff line number Diff line change
@@ -1,65 +1,103 @@
<template>
<table class="w-full">
<tbody>
<ComparisonHeader
<ComparisonHeader :studies="studies" @select-studies="emit('select-studies', $event)" />
<ComparisonSection
:studies="studies"
@select-studies="emit('select-studies',$event)"
:indicators="comparisonConfig.economics"
title="Macro-Economic Indicators"
/>
<ComparisonEconomics :studies="studies" />


<ComparisonSeparator :studies="studies" />

<ComparisonSocial :studies="studies" />

<ComparisonSection
:studies="studies"
:indicators="comparisonConfig.social"
title="Social Sustainability"
/>

<ComparisonSeparator :studies="studies" />

<ComparisonEnvironment :studies="studies" />

<ComparisonSection
:studies="studies"
:indicators="comparisonConfig.environment"
title="Environmental Indicators"
/>
<tr>
<td>
<a class="download" @click="() => downloadComparisonXlsx(studies)">
<div class="download-logo">
<Svg :svg="DowloadLogo" />
</div>
Download comparison data as xlsx</a
>
</td>
</tr>
</tbody>
</table>
</template>

<script setup>
import ComparisonHeader from '@components/comparison/ComparisonHeader.vue'
import ComparisonSocial from '@components/comparison/ComparisonSocial.vue'
import ComparisonSeparator from '@components/comparison/ComparisonSeparator.vue'
import ComparisonEnvironment from './comparison/ComparisonEnvironment.vue'
import ComparisonEconomics from './comparison/ComparisonEconomics.vue'
import { comparisonConfig } from './comparison/comparisonConfig'
import ComparisonSection from './comparison/ComparisonSection.vue'
import Svg from '@components/Svg.vue'
import DowloadLogo from '../images/icons/download.svg'
import { downloadComparisonXlsx } from './comparison/export'
defineProps({
studies: Array,
studies: Array
})
const emit = defineEmits(["select-studies"]);
const emit = defineEmits(['select-studies'])
</script>

<style lang="scss" scoped>
table {
margin: 0 -15px;
table {
margin: 0 -15px;
}
:deep(*) {
td {
box-sizing: border-box;
min-width: 20%;
}
:deep(*) {
td {
box-sizing: border-box;
min-width: 20%;
}
tr td:not(:first-child):not(:last-child):not(:nth-child(2)) {
.default-comparison-cell {
border-left: none;
}
tr td:not(:first-child):not(:last-child):not(:nth-child(2)) {
.default-comparison-cell {
border-left: none;
}
tr td:not(:first-child):not(:last-child):not(:nth-last-child(2)) {
@apply border-r-2;
border-right-color: #D1D5DB;
}
tr td:not(:first-child):not(:last-child):not(:nth-last-child(2)) {
@apply border-r-2;
border-right-color: #d1d5db;
.default-comparison-cell {
border-right: none;
}
.default-comparison-cell {
border-right: none;
}
}
tr td:first-child > * {
margin-left: 15px;
}
tr td:nth-last-child(2) > *:last-child {
margin-right: 15px;
}
tr td:first-child > * {
margin-left: 15px;
}
tr td:nth-last-child(2) > *:last-child {
margin-right: 15px;
}
}
.download {
margin-top: 30px;
display: flex;
gap: 3px;
cursor: pointer;
color: #3f83f8;
align-items: baseli;
&:hover {
color: #1c64f2;
}
}
.download-logo {
height: 20px;
width: 20px;
}
</style>
81 changes: 42 additions & 39 deletions src/components/comparison/ComparisonDefaultCell.vue
Original file line number Diff line number Diff line change
@@ -1,78 +1,81 @@
<template>
<div class="default-comparison-cell" :class="[valueClass, { 'light': lightVersion }]">
<div class="default-comparison-cell" :class="[valueClass, { light: lightVersion }]">
{{ formatedValue }}
</div>
</template>

<script setup>
import { computed } from 'vue';
import { computed } from 'vue'
import { formatNumber, formatPercent, useCurrencyUtils } from '@utils/format.js'
import { isCurrencySupported } from "@utils/currency";
import { isCurrencySupported } from '@utils/currency'
const props = defineProps({
value: Number,
studyData: Object,
preferredCurrency: String,
valueType: {
type: String,
validator: (valueType) => ["amount", "percent", "number"].includes(valueType)
},
lightVersion: Boolean
value: Number,
studyData: Object,
valueType: {
type: String,
validator: (valueType) => ['amount', 'percent', 'number'].includes(valueType)
},
lightVersion: Boolean
})
const valueClass = computed(() => {
if (! props.value) { return "gray" }
return "blue";
});
if (!props.value) {
return 'gray'
}
return 'blue'
})
const displayCurrency = computed(() => {
if(props.valueType !== "amount") { return null; }
return isCurrencySupported(props.studyData.targetCurrency) ? props.preferredCurrency : props.studyData.targetCurrency;
});
if (props.valueType !== 'amount') {
return null
}
return isCurrencySupported(props.studyData.targetCurrency)
? 'USD'
: props.studyData.targetCurrency
})
const { prettyAmount, convertAmount } = useCurrencyUtils({
studyData: props.studyData,
currency: displayCurrency.value
});
})
const formatedValue = computed(() => {
if (! props.value && typeof props.value !== "number") {
return "-";
if (!props.value && typeof props.value !== 'number') {
return '-'
}
switch (props.valueType) {
case "percent":
return formatPercent(props.value);
case "number":
return formatNumber(props.value);
case "amount":
return prettyAmount.value(convertAmount.value(props.value));
case 'percent':
return formatPercent(props.value)
case 'number':
return formatNumber(props.value)
case 'amount':
return prettyAmount.value(convertAmount.value(props.value))
default:
throw new Error("Unrecognized valueType");
throw new Error('Unrecognized valueType')
}
});
})
</script>

<style scoped lang="scss">
.default-comparison-cell {
&.gray {
background-color: #D1D5DB;
color: #6B7280;
background-color: #d1d5db;
color: #6b7280;
&.light {
background-color: #D1D5DB20;
border: #D1D5DB solid 2px;
background-color: #d1d5db20;
border: #d1d5db solid 2px;
}
}
&.blue {
background-color: #A4CAFE;
background-color: #a4cafe;
&.light {
border: #A4CAFE solid 2px;
background-color: #A4CAFE20;
border: #a4cafe solid 2px;
background-color: #a4cafe20;
}
}
}
Expand Down
Loading

0 comments on commit afa0268

Please sign in to comment.