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

fix: refactor: BouquetDetailView dataset description #311

Merged
merged 2 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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