Skip to content

Commit

Permalink
Merge pull request #311 from opendatateam/fix/bouquet-detail-desc
Browse files Browse the repository at this point in the history
fix: refactor: BouquetDetailView dataset description
  • Loading branch information
abulte committed Jan 8, 2024
2 parents a274f7e + 570b38a commit 48a81ce
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 76 deletions.
4 changes: 2 additions & 2 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const quickLinks = computed(() => {
const userLink = {
label: isLoggedIn.value
? `${store.$state.data.first_name} ${store.$state.data.last_name}`
? `${store.$state.data?.first_name} ${store.$state.data?.last_name}`
: 'Se connecter',
icon: isLoggedIn.value ? 'ri-logout-box-r-line' : 'ri-account-circle-line',
to: isLoggedIn.value ? '/logout' : '/login',
Expand Down Expand Up @@ -106,7 +106,7 @@ const footerMandatoryLinks = ref(config.website.footer_mandatory_links)
:badge-text="badgeText"
:badge-style="badgeStyle"
@search="doSearch"
@update:modelValue="updateQuery"
@update:model-value="updateQuery"
>
<template #mainnav="{ hidemodal }">
<Navigation :on-click="hidemodal" />
Expand Down
65 changes: 33 additions & 32 deletions src/custom/ecospheres/views/bouquets/BouquetDetailView.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
<script setup>
<script setup lang="ts">
import { onMounted, ref, computed } from 'vue'
import type { Ref } from 'vue'
import { useLoading } from 'vue-loading-overlay'
import { useRoute, useRouter } from 'vue-router'
import { useRouter } from 'vue-router'
import DiscussionsList from '@/components/DiscussionsList.vue'
import config from '@/config'
import { Availability, isAvailable } from '@/model'
import { Availability, isAvailable, type Theme, type Topic } from '@/model'
import { useRouteParamsAsString } from '@/router/utils'
import { useTopicStore } from '@/store/TopicStore'
import { useUserStore } from '@/store/UserStore'
import { descriptionFromMarkdown } from '@/utils'
import { descriptionFromMarkdown, fromMarkdown } from '@/utils'
const route = useRoute()
const route = useRouteParamsAsString()
const router = useRouter()
const store = useTopicStore()
const userStore = useUserStore()
const bouquet = ref({})
const bouquet: Ref<Topic | null> = ref(null)
const theme = ref()
const subtheme = ref()
const loading = useLoading()
Expand Down Expand Up @@ -48,25 +50,25 @@ const copyUrl = () => {
navigator.clipboard.writeText(url)
}
const getTheme = (themeName) => {
return config.themes.find((theme) => theme.name === themeName)
const getTheme = (themeName: string): Theme => {
return config.themes.find((theme: Theme) => theme.name === themeName)
}
const convertToHex = (hex, color) => {
return hex ? `#${parseInt(hex, 16).toString(16).padStart(6, '0')}` : color
const convertToHex = (hex: string): string => {
return `#${parseInt(hex, 16).toString(16).padStart(6, '0')}`
}
const getThemeColor = (themeName) => {
const getThemeColor = (themeName: string): string => {
const theme = getTheme(themeName)
return convertToHex(theme ? theme.color : 'transparent')
return theme.color ? convertToHex(theme.color) : 'transparent'
}
const getTextColor = (themeName) => {
const getTextColor = (themeName: string): string => {
const theme = getTheme(themeName)
return convertToHex(theme ? theme.textColor : '#000000b3')
return theme.textColor ? convertToHex(theme.textColor) : '#000000b3'
}
const getSelectedThemeColor = (themed) => {
const getSelectedThemeColor = (themed: string) => {
selectedTheme.value = themed
return getThemeColor(selectedTheme.value)
}
Expand All @@ -75,7 +77,7 @@ const canCreate = computed(() => {
return (
userStore.isAdmin() ||
(userStore.$state.isLoggedIn &&
bouquet.value.owner?.id === userStore.$state.data?.id)
bouquet.value?.owner?.id === userStore.$state.data?.id)
)
})
Expand All @@ -85,22 +87,22 @@ onMounted(() => {
.load(route.params.bid)
.then((res) => {
bouquet.value = res
theme.value =
bouquet.value.extras[`${config.universe.name}:informations`][0].theme
theme.value = bouquet.value?.extras['ecospheres:informations'][0].theme
subtheme.value =
bouquet.value.extras[`${config.universe.name}:informations`][0].subtheme
bouquet.value?.extras['ecospheres:informations'][0].subtheme
breadcrumbLinks.value.push(
{
text: theme,
text: theme.value,
to: `/bouquets/?theme=${theme.value}`
},
{
text: subtheme.value,
to: `/bouquets/?theme=${theme.value}&subtheme=${subtheme.value}`
},
{
text: bouquet.value.name
to: '',
text: bouquet.value?.name ?? ''
}
)
})
Expand Down Expand Up @@ -132,9 +134,9 @@ onMounted(() => {
</DsfrButton>
<div class="bouquet__header fr-mb-4w">
<div class="bouquet__header__left">
<h3 class="fr-mb-3w fr-mb-md-0 fr-mr-md-3w">{{ bouquet.name }}</h3>
<h3 class="fr-mb-3w fr-mb-md-0 fr-mr-md-3w">{{ bouquet?.name }}</h3>
<DsfrTag
v-if="bouquet.extras"
v-if="bouquet?.extras"
class="fr-mb-3w fr-mb-md-0 bold uppercase"
:label="subtheme"
:style="{
Expand All @@ -156,20 +158,18 @@ onMounted(() => {
<div v-html="description" />
<div
v-if="
bouquet.extras &&
bouquet.extras[`${config.universe.name}:datasets_properties`]
bouquet?.extras && bouquet.extras['ecospheres:datasets_properties']
"
>
<h5>
Données utilisées ({{
bouquet.extras[`${config.universe.name}:datasets_properties`]
.length
bouquet?.extras['ecospheres:datasets_properties'].length
}})
</h5>
<DsfrAccordionsGroup>
<li
v-for="(datasetProperties, idx) in bouquet.extras[
`${config.universe.name}:datasets_properties`
v-for="(datasetProperties, idx) in bouquet?.extras[
'ecospheres:datasets_properties'
]"
:key="idx"
>
Expand All @@ -191,7 +191,8 @@ onMounted(() => {
}`"
/>
<div class="fr-mb-3w">
{{ datasetProperties.description }}
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-html="fromMarkdown(datasetProperties.purpose)"></span>
</div>
<div class="button__wrapper">
<a
Expand All @@ -204,7 +205,7 @@ onMounted(() => {
<a
v-else
class="fr-btn fr-btn--secondary inline-flex"
:href="datasetProperties.uri"
:href="datasetProperties.uri ?? undefined"
target="_blank"
>Accéder au catalogue</a
>
Expand All @@ -226,7 +227,7 @@ onMounted(() => {
<div class="fr-mt-8w">
<DiscussionsList
v-if="showDiscussions"
v-if="showDiscussions && bouquet"
:subject="bouquet"
subject-class="Topic"
/>
Expand Down
9 changes: 5 additions & 4 deletions src/model/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { Owned } from '@etalab/data.gouv.fr-components'

interface DatasetProperties {
title: string
purpose: string
uri: string | null
id: string | null
availability: string // must be one of Availability value
availability: Availability
}

enum Availability {
Expand All @@ -23,6 +25,7 @@ interface Theme {
name: string
color: string
subthemes: Subtheme[]
textColor?: string
}

interface Subtheme {
Expand Down Expand Up @@ -64,15 +67,13 @@ interface TopicExtras {
['ecospheres:datasets_properties']: DatasetProperties[]
}

interface Topic {
type Topic = Owned & {
name: string
description: string
created_at: string
extras: TopicExtras
featured: boolean
id: string
organisation: any
owner: any
page: string
private: boolean
reuses: []
Expand Down
45 changes: 15 additions & 30 deletions src/store/TopicStore.js → src/store/TopicStore.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import { defineStore } from 'pinia'

import config from '@/config'
import type { Topic } from '@/model'

import TopicsAPI from '../services/api/resources/TopicsAPI'

const topicsAPI = new TopicsAPI()
const topicsAPIv2 = new TopicsAPI({ version: 2 })

export interface RootState {
data: Topic[]
}

export const useTopicStore = defineStore('topic', {
state: () => ({
state: (): RootState => ({
data: []
}),
actions: {
/**
* Load topics to store from a list of ids and API
*
* @param {*} topics
*/
async loadTopicsFromList(topics) {
async loadTopicsFromList(topics: Topic[]) {
this.data = []
for (const topic of topics) {
const res = await topicsAPI.get(topic.id)
Expand All @@ -26,26 +29,21 @@ export const useTopicStore = defineStore('topic', {
},
/**
* Filter a list of topics related to the current universe
*
* @param {Array} topics
* @returns {Array}
*/
filter(topics) {
filter(topics: Topic[]) {
return topics.filter((topic) => topic.id !== config.universe.topic_id)
},
/**
* Load universe related topics from API
*
* @returns {Promise<object[]>}
*/
async loadTopicsForUniverse() {
async loadTopicsForUniverse(): Promise<Topic[]> {
if (this.data.length > 0) return this.data
let response = await topicsAPIv2.list({
page_size: config.website.pagination_sizes.topics_list,
tag: config.universe.name
})
this.data = this.filter(response.data)
while (response.next_page) {
while (response.next_page !== null) {
response = await topicsAPIv2.request({
url: response.next_page,
method: 'get'
Expand All @@ -56,43 +54,30 @@ export const useTopicStore = defineStore('topic', {
},
/**
* Get a topic from store
*
* @param {string} slugOrId
* @returns {object}
*/
get(slugOrId) {
get(slugOrId: string): Topic | undefined {
return this.data.find((b) => b.slug === slugOrId || b.id === slugOrId)
},
/**
* Get a single topic from store or API
*
* @param {string} slugOrId
* @returns {object}
*/
async load(slugOrId) {
async load(slugOrId: string) {
const existing = this.get(slugOrId)
if (existing) return existing
if (existing !== undefined) return existing
return await topicsAPIv2.get(slugOrId)
},
/**
* Create a topic
*
* @param {object} topic
* @returns {object}
*/
async create(topic) {
async create(topic: Topic): Promise<Topic> {
const res = await topicsAPI.create(topic)
this.data.push(res)
return res
},
/**
* Update a topic
*
* @param {string} topicId
* @param {object} data
* @returns {object}
*/
async update(topicId, data) {
async update(topicId: string, data: Topic): Promise<Topic> {
const res = await topicsAPI.update(topicId, data)
const idx = this.data.findIndex((b) => b.id === topicId)
this.data[idx] = res
Expand Down
23 changes: 15 additions & 8 deletions src/store/UserStore.js → src/store/UserStore.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import type { User } from '@etalab/data.gouv.fr-components'
import { defineStore } from 'pinia'

// FIXME: we cant use UserAPI here (circular dep?)
// maybe try to use the service that will use the API

const STORAGE_KEY = 'token'

export interface RootState {
isLoggedIn: boolean
token: string | null
data: User | null
}

export const useUserStore = defineStore('user', {
state: () => ({
state: (): RootState => ({
isLoggedIn: false,
data: {},
token: undefined
data: null,
token: null
}),
getters: {
loggedIn(state) {
Expand All @@ -22,15 +29,15 @@ export const useUserStore = defineStore('user', {
*/
init() {
const token = localStorage.getItem(STORAGE_KEY)
if (token) {
if (token !== null) {
this.token = token
this.isLoggedIn = true
}
},
/**
* Store user info after login
*/
login(token) {
login(token: string) {
this.isLoggedIn = true
this.token = token
localStorage.setItem(STORAGE_KEY, token)
Expand All @@ -40,14 +47,14 @@ export const useUserStore = defineStore('user', {
*/
logout() {
this.isLoggedIn = false
this.token = undefined
this.data = {}
this.token = null
this.data = null
localStorage.removeItem(STORAGE_KEY)
},
/**
* Store user infos
*/
storeInfo(data) {
storeInfo(data: User) {
this.data = data
},
/**
Expand Down
Loading

0 comments on commit 48a81ce

Please sign in to comment.