Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow adding the same dataset multiple times to a bouquet. #507

Merged
merged 11 commits into from
Sep 12, 2024
12 changes: 10 additions & 2 deletions src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
font-weight: 600;
font-family: Spectral;
font-style: italic;
src: url("./fonts/Spectral-Italic.woff2") format("woff2"),
url("./fonts/Spectral-Italic.woff") format("woff");
src: url('./fonts/Spectral-Italic.woff2') format('woff2'),
url('./fonts/Spectral-Italic.woff') format('woff');

font-display: swap;
}
Expand Down Expand Up @@ -111,3 +111,11 @@ textarea {
.datagouv-components h1 {
margin-bottom: 1.5rem;
}

/* FIXME */

/* https://github.com/datagouv/udata-front/issues/499 */
.fr-badge.fr-badge--info.fr-badge--info::before {
narduin marked this conversation as resolved.
Show resolved Hide resolved
-webkit-mask-image: url(/node_modules/@gouvfr/dsfr/dist/icons/system/fr--info-fill.svg);
mask-image: url(/node_modules/@gouvfr/dsfr/dist/icons/system/fr--info-fill.svg);
}
35 changes: 26 additions & 9 deletions src/components/datasets/DatasetAddToBouquetModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useLoading } from 'vue-loading-overlay'

import Tooltip from '@/components/TooltipWrapper.vue'
import DatasetPropertiesTextFields from '@/components/forms/dataset/DatasetPropertiesTextFields.vue'
import { Availability, type DatasetProperties, type Topic } from '@/model/topic'
import { Availability, type DatasetProperties } from '@/model/topic'
import { useTopicStore } from '@/store/TopicStore'
import { useTopicsConf } from '@/utils/config'

Expand Down Expand Up @@ -42,8 +42,7 @@ const bouquetOptions = computed(() => {
return bouquets.value.map((bouquet) => {
return {
value: bouquet.id,
text: bouquet.name,
disabled: isDatasetInBouquet(bouquet)
text: bouquet.name
}
})
})
Expand Down Expand Up @@ -75,12 +74,17 @@ const modalActions = computed(() => {
]
})

const isDatasetInBouquet = (bouquet: Topic): boolean => {
const datasetsProperties = bouquet.extras[topicsExtrasKey].datasets_properties
return datasetsProperties.some(
const isDatasetInBouquet = computed(() => {
narduin marked this conversation as resolved.
Show resolved Hide resolved
if (selectedBouquetId.value === null) {
return false
}
const selectedBouquet = topicStore.get(selectedBouquetId.value)
const datasetsProperties =
selectedBouquet?.extras[topicsExtrasKey].datasets_properties
return datasetsProperties?.some(
(datasetProps) => datasetProps.id === props.dataset.id
)
}
})

const submit = async () => {
if (selectedBouquetId.value === null) {
Expand Down Expand Up @@ -136,14 +140,27 @@ onMounted(() => {
{{ capitalize(topicsName) }} à associer
<span class="required">&nbsp;*</span>
<Tooltip
:text="`Choisissez parmi les ${topicsName}s dont vous êtes l'auteur. Si un ${topicsName} apparait désactivé, c'est que le jeu de données y est déjà associé.`"
:text="`Choisissez parmi les ${topicsName}s dont vous êtes l'auteur.`"
/>
</template>
</DsfrSelect>

<DsfrBadge
v-if="isDatasetInBouquet"
type="info"
label="Déjà utilisé dans ce bouquet"
small
ellipsis
class="fr-mb-2w"
/>
<DatasetPropertiesTextFields
v-if="topicsDatasetEditorialization"
v-model:dataset-properties="datasetProperties"
/>
</DsfrModal>
</template>

<style scoped>
.fr-select-group:has(+ .fr-badge) {
margin-bottom: 0.5rem;
}
</style>
43 changes: 40 additions & 3 deletions src/components/forms/dataset/DatasetCardForSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ const props = defineProps({
dataset: {
type: Object as () => DatasetV2,
required: true
},
alreadySelected: {
type: Boolean,
required: false,
default: false
},
badgePosition: {
type: String as () => 'relative' | 'absolute' | 'sr-only',
required: false,
default: 'relative'
}
})

Expand All @@ -23,14 +33,38 @@ const thumbnail = computed(() => {
return props.dataset.organization.logo_thumbnail
return getOwnerAvatar(props.dataset)
})

const badgeClasse = computed(() => {
let classes: string = ''
switch (props.badgePosition) {
case 'relative':
classes = 'relative fr-mb-2v'
break
case 'absolute':
classes = 'absolute top-0 fr-mt-n4v'
break
case 'sr-only':
classes = 'fr-sr-only'
break
}
return classes
})
</script>

<template>
<div>
<div class="card">
<DsfrBadge
v-if="alreadySelected"
type="info"
label="Déjà utilisé dans ce bouquet"
small
ellipsis
:class="badgeClasse"
/>
<div class="fr-grid-row fr-grid-row--gutters fr-grid-row--top">
<div class="fr-col-auto">
<div class="logo">
<img :src="thumbnail" width="60" height="60" />
<img :src="thumbnail" width="60" height="60" alt="" />
</div>
</div>
<div class="fr-col">
Expand Down Expand Up @@ -64,7 +98,10 @@ const thumbnail = computed(() => {
</div>
</template>

<style lang="scss" scoped>
<style scoped>
.card:has(.fr-badge.absolute) {
padding-top: 1rem;
}
h4 {
font-size: 1rem;
}
Expand Down
18 changes: 14 additions & 4 deletions src/components/forms/dataset/SelectDataset.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const search = debounce(async (query: string) => {
await new SearchAPI().search(query, null, 1, {
page_size: 10
})
).data.filter((dataset) => !alreadySelected(dataset.id))
).data
options.value = datasets
isLoading.value = false
}, 400)
Expand All @@ -69,7 +69,6 @@ const clear = () => {
:loading="isLoading"
:clear-on-select="true"
:close-on-select="true"
:max-height="600"
:show-no-results="false"
:hide-selected="true"
@search-change="search"
Expand All @@ -82,16 +81,27 @@ const clear = () => {
></div>
</template>
<template #singleLabel="slotProps">
<DatasetCardForSelect :dataset="slotProps.option" />
<DatasetCardForSelect
:dataset="slotProps.option"
:already-selected="alreadySelected(slotProps.option.id)"
badge-position="absolute"
/>
</template>
<template #option="slotProps">
<DatasetCardForSelect :dataset="slotProps.option" />
<DatasetCardForSelect
:dataset="slotProps.option"
:already-selected="alreadySelected(slotProps.option.id)"
badge-position="relative"
/>
</template>
<template #noOptions> Précisez ou élargissez votre recherche </template>
</Multiselect>
</template>

<style scoped>
.multiselect {
margin-top: 1rem;
}
:deep(.multiselect__option::after) {
clip: rect(0 0 0 0);
clip-path: inset(50%);
Expand Down
15 changes: 12 additions & 3 deletions src/views/bouquets/BouquetDetailView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,22 @@ const onUpdateDatasets = () => {
throw Error('Trying to update null topic')
}
const loader = useLoading().show()

// Deduplicate datasets ids in case of same DS used multiple times
// API rejects PUT if the same id is used more than once
const dedupedDatasets = [
...new Set(
datasetsProperties.value
.filter((d) => d.id !== null && d.remoteDeleted !== true)
.map((d) => d.id)
)
]

store
.update(topic.value.id, {
// send the tags or payload will be rejected
tags: topic.value.tags,
datasets: datasetsProperties.value
.filter((d) => d.id !== null && d.remoteDeleted !== true)
.map((d) => d.id),
datasets: dedupedDatasets,
extras: updateTopicExtras(topic.value, {
datasets_properties: datasetsProperties.value.map(
({ remoteDeleted, ...data }) => data
Expand Down
Loading