From 0953304c8f087ee9ddfe662a580d9162ffb536b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Bult=C3=A9?= Date: Tue, 2 Apr 2024 08:35:01 +0200 Subject: [PATCH] feat(bouquet): show reuses --- src/components/ReusesList.vue | 110 ++++++++++++++ .../views/bouquets/BouquetDetailView.vue | 15 +- src/model/index.ts | 5 +- src/model/resource.ts | 4 + src/model/reuse.ts | 26 ++++ src/services/api/resources/ReusesAPI.js | 21 --- src/services/api/resources/ReusesAPI.ts | 41 ++++++ src/store/ReuseStore.js | 50 ------- src/store/ReuseStore.ts | 47 ++++++ src/store/TopicStore.ts | 2 +- src/utils/index.js | 17 ++- src/views/datasets/DatasetDetailView.vue | 134 +++--------------- 12 files changed, 278 insertions(+), 194 deletions(-) create mode 100644 src/components/ReusesList.vue create mode 100644 src/model/reuse.ts delete mode 100644 src/services/api/resources/ReusesAPI.js create mode 100644 src/services/api/resources/ReusesAPI.ts delete mode 100644 src/store/ReuseStore.js create mode 100644 src/store/ReuseStore.ts diff --git a/src/components/ReusesList.vue b/src/components/ReusesList.vue new file mode 100644 index 000000000..7fab02eca --- /dev/null +++ b/src/components/ReusesList.vue @@ -0,0 +1,110 @@ + + + diff --git a/src/custom/ecospheres/views/bouquets/BouquetDetailView.vue b/src/custom/ecospheres/views/bouquets/BouquetDetailView.vue index f933a08d8..fa03973ac 100644 --- a/src/custom/ecospheres/views/bouquets/BouquetDetailView.vue +++ b/src/custom/ecospheres/views/bouquets/BouquetDetailView.vue @@ -9,6 +9,7 @@ import { useLoading } from 'vue-loading-overlay' import { useRouter } from 'vue-router' import DiscussionsList from '@/components/DiscussionsList.vue' +import ReusesList from '@/components/ReusesList.vue' import config from '@/config' import BouquetDatasetList, { getDatasetListTitle @@ -189,7 +190,11 @@ onMounted(() => { { subject-class="Topic" /> + + + + diff --git a/src/model/index.ts b/src/model/index.ts index 918f4d86f..828e2259b 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -1,4 +1,4 @@ -import type { Owned } from '@etalab/data.gouv.fr-components' +import type { Owned, Rel } from '@etalab/data.gouv.fr-components' import type { SpatialField } from './spatial' @@ -87,7 +87,8 @@ type Topic = Owned & { id: string page: string private: boolean - reuses: [] + reuses: Rel + datasets: Rel slug: string tags: string[] uri: string diff --git a/src/model/resource.ts b/src/model/resource.ts index f10b0aeaa..05377a9fd 100644 --- a/src/model/resource.ts +++ b/src/model/resource.ts @@ -13,4 +13,8 @@ export interface ResourceData { type: ResourceType } +export interface ResourceDataWithQuery extends ResourceData { + query: string +} + export type { Resource } diff --git a/src/model/reuse.ts b/src/model/reuse.ts new file mode 100644 index 000000000..d0641a18a --- /dev/null +++ b/src/model/reuse.ts @@ -0,0 +1,26 @@ +import type { Owned } from '@etalab/data.gouv.fr-components' + +import type { GenericResponse } from './api' + +export type Reuse = Owned & { + id: string + title: string + created_at: string + image_thumbnail?: string + type: string + page: string +} + +export interface ReuseResponse extends GenericResponse { + data: Reuse[] +} + +export interface ReuseType { + id: string + label: string +} + +export enum ReuseModel { + dataset = 'jeu de donnée', + topic = 'bouquet' +} diff --git a/src/services/api/resources/ReusesAPI.js b/src/services/api/resources/ReusesAPI.js deleted file mode 100644 index 9a7753fd1..000000000 --- a/src/services/api/resources/ReusesAPI.js +++ /dev/null @@ -1,21 +0,0 @@ -import DatagouvfrAPI from '../DatagouvfrAPI' - -export default class ReusesAPI extends DatagouvfrAPI { - endpoint = 'reuses' - - /** - * Get reuses for a dataset - * - * @param {str} dataset_id - * @returns {object} - */ - async getReuses(datasetId) { - return this.request({ - url: this.url(true), - method: 'get', - params: { - dataset: datasetId - } - }) - } -} diff --git a/src/services/api/resources/ReusesAPI.ts b/src/services/api/resources/ReusesAPI.ts new file mode 100644 index 000000000..a22904ada --- /dev/null +++ b/src/services/api/resources/ReusesAPI.ts @@ -0,0 +1,41 @@ +import type { Rel } from '@etalab/data.gouv.fr-components' + +import type { Reuse, ReuseResponse } from '@/model/reuse' + +import DatagouvfrAPI from '../DatagouvfrAPI' + +export default class ReusesAPI extends DatagouvfrAPI { + endpoint = 'reuses' + + /** + * Get reuses for a dataset + */ + async getReusesForDataset(datasetId: string): Promise { + return await this.request({ + url: this.url(true), + method: 'get', + params: { + dataset: datasetId + } + }) + } + + /** + * Get reuses from rel + */ + async getReusesFromRel(rel: Rel): Promise { + let response = await this.request({ + url: rel.href, + method: 'get' + }) + let reuses = response.data + while (response.next_page !== null) { + response = await this.request({ + url: response.next_page, + method: 'get' + }) + reuses = [...reuses, ...response.data] + } + return reuses + } +} diff --git a/src/store/ReuseStore.js b/src/store/ReuseStore.js deleted file mode 100644 index 49f36fb78..000000000 --- a/src/store/ReuseStore.js +++ /dev/null @@ -1,50 +0,0 @@ -import { defineStore } from 'pinia' - -import ReusesAPI from '../services/api/resources/ReusesAPI' - -const reusesAPI = new ReusesAPI() - -export const useReuseStore = defineStore('reuse', { - state: () => ({ - data: {} - }), - actions: { - /** - * Get reuses for a dataset from store - * - * @param {str} dataset_id - * @returns {Array} - */ - getReusesForDataset(dataset_id) { - return this.data[dataset_id] - }, - /** - * Async function to trigger API fetch of reuses for a dataset - * - * @param {string} org_id - * @param {number} page - * @returns {Array} - */ - async loadReusesForDataset(dataset_id) { - const existing = this.getReusesForDataset(dataset_id) - if (existing) return existing - const reuses = await reusesAPI.getReuses(dataset_id) - this.addReuses(dataset_id, reuses.data) - return this.getReusesForDataset(dataset_id) - }, - /** - * Store the result of a reuses fetch operation for a dataset in store - * - * @param {string} dataset_id - * @param {Array} res - */ - addReuses(dataset_id, res) { - if (!res) return - this.data[dataset_id] = res - }, - - async getTypes() { - return await reusesAPI.get('types') - } - } -}) diff --git a/src/store/ReuseStore.ts b/src/store/ReuseStore.ts new file mode 100644 index 000000000..212726224 --- /dev/null +++ b/src/store/ReuseStore.ts @@ -0,0 +1,47 @@ +import { defineStore } from 'pinia' + +import type { Reuse, ReuseType } from '@/model/reuse' + +import ReusesAPI from '../services/api/resources/ReusesAPI' + +const reusesAPI = new ReusesAPI() + +interface RootState { + data: Record +} + +export const useReuseStore = defineStore('reuse', { + state: (): RootState => ({ + data: {} + }), + actions: { + /** + * Get reuses for a dataset from store + */ + getReusesForDataset(datasetId: string) { + return this.data[datasetId] + }, + /** + * Async function to trigger API fetch of reuses for an object + */ + async loadReusesForDataset(datasetId: string) { + const existing = this.getReusesForDataset(datasetId) + if (existing !== undefined) return existing + const reuses = await reusesAPI.getReusesForDataset(datasetId) + this.addReuses(datasetId, reuses.data) + return this.getReusesForDataset(datasetId) + }, + /** + * Store the result of a reuses fetch operation for a dataset in store + */ + addReuses(datasetId: string, reuses: Reuse[]) { + this.data[datasetId] = reuses + }, + /** + * Get reuses types from the API + */ + async getTypes(): Promise { + return await reusesAPI.get('types') + } + } +}) diff --git a/src/store/TopicStore.ts b/src/store/TopicStore.ts index affdc5ba7..4b2924da3 100644 --- a/src/store/TopicStore.ts +++ b/src/store/TopicStore.ts @@ -66,7 +66,7 @@ export const useTopicStore = defineStore('topic', { async loadTopicsFromList(topics: Topic[]) { this.data = [] for (const topic of topics) { - const res = await topicsAPI.get(topic.id) + const res = await topicsAPIv2.get(topic.id) this.data.push(res) } }, diff --git a/src/utils/index.js b/src/utils/index.js index 5834f17c8..453c74dc7 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -39,10 +39,17 @@ export const stripFromMarkdown = (value) => { * Format date * */ -export const formatDate = (dateString) => { +export const formatDate = (dateString, short = false) => { const date = new Date(dateString) - return new Intl.DateTimeFormat('default', { - dateStyle: 'full', - timeStyle: 'short' - }).format(date) + const params = short + ? { + day: 'numeric', + month: 'short', + year: 'numeric' + } + : { + dateStyle: 'full', + timeStyle: 'short' + } + return new Intl.DateTimeFormat('default', params).format(date) } diff --git a/src/views/datasets/DatasetDetailView.vue b/src/views/datasets/DatasetDetailView.vue index 71918207b..9855cd37b 100644 --- a/src/views/datasets/DatasetDetailView.vue +++ b/src/views/datasets/DatasetDetailView.vue @@ -17,11 +17,11 @@ import DatasetAddToBouquetModal from '@/custom/ecospheres/components/datasets/Da import ChartData from '../../components/ChartData.vue' import DiscussionsList from '../../components/DiscussionsList.vue' -import type { ResourceData } from '../../model/resource' +import ReusesList from '../../components/ReusesList.vue' +import type { ResourceDataWithQuery } from '../../model/resource' import { useRouteParamsAsString } from '../../router/utils' import { useDatasetStore } from '../../store/DatasetStore' import { useResourceStore } from '../../store/ResourceStore' -import { useReuseStore } from '../../store/ReuseStore' import { useUserStore } from '../../store/UserStore' import { descriptionFromMarkdown, formatDate } from '../../utils' @@ -30,34 +30,27 @@ const datasetId = route.params.did const datasetStore = useDatasetStore() const resourceStore = useResourceStore() -const reuseStore = useReuseStore() const userStore = useUserStore() const dataset = computed(() => datasetStore.get(datasetId)) -const reuses = ref([]) -const resources = ref>({}) +const resources = ref>({}) const selectedTabIndex = ref(0) const license = ref() -const types = ref([]) const showAddToBouquetModal = ref(false) const pageSize = config.website.pagination_sizes.files_list const showDiscussions = config.website.discussions.dataset.display -const updateQuery = (q, typeId) => { +const updateQuery = (q: string, typeId: string) => { resources.value[typeId].query = q changePage(typeId, 1, q) } -const doSearch = (typeId) => { +const doSearch = (typeId: string) => { changePage(typeId, 1, resources.value[typeId].query) } -onMounted(() => { - datasetStore.load(datasetId) -}) - const chartData = computed(() => { if (!dataset.value?.extras) return return dataset.value.extras['config:charts'] @@ -99,50 +92,13 @@ const changePage = (type: string, page = 1, query = '') => { return resourceStore .fetchDatasetResources(dataset.value.id, type, page, query) .then((data) => { - resources.value[type].resources = data['data'] - resources.value[type].total = data['total'] + resources.value[type].resources = data.data + resources.value[type].total = data.total }) } } -const simpleDate = (dateString) => { - const date = new Date(dateString) - return new Intl.DateTimeFormat('default', { - day: 'numeric', - month: 'short', - year: 'numeric' - }).format(date) -} - -const cropString = (string) => { - if (string.length <= 40) { - return string - } else { - return string.slice(0, 40) + '...' - } -} - -const reuseDescription = (r) => { - if (r.organization?.name) { - return ( - 'Publié le ' + - simpleDate(r.created_at) + - ' par ' + - r.organization?.name || r.owner.first_name + ' ' + r.owner.last_name - ) - } else { - return ( - 'Publié le ' + - simpleDate(r.created_at) + - ' par ' + - r.owner.first_name + - ' ' + - r.owner.last_name - ) - } -} - -const getResourcesTitle = (typedResources: ResourceData) => { +const getResourcesTitle = (typedResources: ResourceDataWithQuery) => { if (typedResources?.total > 1) { let pluralName switch (typedResources.type.id) { @@ -173,11 +129,6 @@ const getResourcesTitle = (typedResources: ResourceData) => { } } -const getType = (id) => { - const type = types.value.find((t) => t.id === id) - return type?.label || '' -} - const discussionWellTitle = showDiscussions ? 'Participer aux discussions' : 'Voir les discussions' @@ -193,17 +144,13 @@ watch( dataset, async () => { if (!dataset.value) return - // fetch reuses - reuseStore - .loadReusesForDataset(dataset.value.id) - .then((r) => (reuses.value = r)) // fetch ressources if need be if (dataset.value.resources.rel) { const resourceLoader = useLoading().show() - const allResources = await resourceStore.loadResources( + const allResources = (await resourceStore.loadResources( dataset.value.id, dataset.value.resources - ) + )) as ResourceDataWithQuery[] for (const typedResources of allResources) { resources.value[typedResources.type.id] = { ...typedResources } resources.value[typedResources.type.id].totalWithoutFilter = @@ -211,13 +158,16 @@ watch( } resourceLoader.hide() } else { - resources.value = dataset.value.resources + throw Error('Unsupported dataset.resources format') } license.value = await datasetStore.getLicense(dataset.value.license) - types.value = await reuseStore.getTypes() }, { immediate: true } ) + +onMounted(() => { + datasetStore.load(datasetId) +}) - -