From 11c81dd1a0abcec6848fe50b74c89c1b620c49e8 Mon Sep 17 00:00:00 2001 From: Nicolas Kempf Le Stanc Date: Tue, 14 Apr 2026 18:02:14 +0200 Subject: [PATCH 001/121] fix(resources): fix wrong checks and refactor to provide a single function as source of truth --- components/Datasets/FileEditModal.vue | 5 ++-- .../ResourceAccordion/ResourceAccordion.vue | 2 +- .../src/composables/useHasTabularData.ts | 23 ++++++++++++------- .../composables/useResourceCapabilities.ts | 4 ++-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/components/Datasets/FileEditModal.vue b/components/Datasets/FileEditModal.vue index 09baceb46..2dd8b0129 100644 --- a/components/Datasets/FileEditModal.vue +++ b/components/Datasets/FileEditModal.vue @@ -228,11 +228,12 @@ const resourceForm = ref(cloneDeep(props.resource)) const open = ref(false) const hasFileChanged = ref(false) -const hasTabularData = useHasTabularData() +// Use the composable for tabular data detection +const { hasTabularData } = useHasTabularData() // Check if resource has tabular API const hasTabularApi = computed(() => { - return props.resource.resource ? hasTabularData(props.resource.resource) : false + return hasTabularData(props.resource.resource || { extras: {}, filetype: undefined }) }) // Watch for file changes diff --git a/datagouv-components/src/components/ResourceAccordion/ResourceAccordion.vue b/datagouv-components/src/components/ResourceAccordion/ResourceAccordion.vue index d28f7e21b..06d076fd5 100644 --- a/datagouv-components/src/components/ResourceAccordion/ResourceAccordion.vue +++ b/datagouv-components/src/components/ResourceAccordion/ResourceAccordion.vue @@ -426,7 +426,7 @@ const DatafairPreview = defineAsyncComponent(() => import('./Datafair.client.vue const { t } = useTranslation() const { formatRelativeIfRecentDate } = useFormatDate() -const checkTabularData = useHasTabularData() +const { hasTabularData: checkTabularData } = useHasTabularData() const hasPreview = computed(() => { // For JSON, PDF, and XML files, show preview. diff --git a/datagouv-components/src/composables/useHasTabularData.ts b/datagouv-components/src/composables/useHasTabularData.ts index 7b02a78ba..107a720ac 100644 --- a/datagouv-components/src/composables/useHasTabularData.ts +++ b/datagouv-components/src/composables/useHasTabularData.ts @@ -1,15 +1,22 @@ import { useComponentsConfig } from '../config' -import type { Resource } from '../types/resources' +/** + * Composable to determine if a resource has tabular data. + * This is used to show the "Données" tab for tabular files AND the "Structure des données" tab (for tabular data structure). + * + * @returns An object containing the hasTabularData function. + */ export const useHasTabularData = () => { const config = useComponentsConfig() - return (resource: Resource) => { - return ( - config.tabularApiUrl - && resource.extras['analysis:parsing:parsing_table'] - && !resource.extras['analysis:parsing:error'] - && (config.tabularAllowRemote || resource.filetype === 'file') - ) + return { + hasTabularData: (resource: { extras: Record, filetype?: string }) => { + return ( + config.tabularApiUrl + && resource.extras['analysis:parsing:parsing_table'] + && !resource.extras['analysis:parsing:error'] + && (config.tabularAllowRemote || resource.filetype === 'file') + ) + }, } } diff --git a/datagouv-components/src/composables/useResourceCapabilities.ts b/datagouv-components/src/composables/useResourceCapabilities.ts index 052e62694..14c4f885b 100644 --- a/datagouv-components/src/composables/useResourceCapabilities.ts +++ b/datagouv-components/src/composables/useResourceCapabilities.ts @@ -17,7 +17,7 @@ export function useResourceCapabilities( ) { const config = useComponentsConfig() const { t } = useTranslation() - const checkTabularData = useHasTabularData() + const { hasTabularData: hasTabularDataFromComposable } = useHasTabularData() const hasPreview = computed(() => { const format = toValue(resource).format?.toLowerCase() @@ -26,7 +26,7 @@ export function useResourceCapabilities( const hasTabularData = computed(() => { const r = toValue(resource) - return checkTabularData(r) + return hasTabularDataFromComposable(r) }) const hasPmtiles = computed(() => { From 3818a3525eeaa191e46a51a0df48c96fff5d137f Mon Sep 17 00:00:00 2001 From: Nicolas Kempf Le Stanc Date: Tue, 24 Feb 2026 12:41:12 +0100 Subject: [PATCH 002/121] fix: condition --- components/Datasets/FileEditModal.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Datasets/FileEditModal.vue b/components/Datasets/FileEditModal.vue index 2dd8b0129..d10963631 100644 --- a/components/Datasets/FileEditModal.vue +++ b/components/Datasets/FileEditModal.vue @@ -233,7 +233,7 @@ const { hasTabularData } = useHasTabularData() // Check if resource has tabular API const hasTabularApi = computed(() => { - return hasTabularData(props.resource.resource || { extras: {}, filetype: undefined }) + return props.resource.resource ? hasTabularData(props.resource.resource) : false }) // Watch for file changes From f193dd22169d2f937e7cdd9e3403ae73923a7912 Mon Sep 17 00:00:00 2001 From: Nicolas Kempf Le Stanc Date: Tue, 24 Feb 2026 12:42:36 +0100 Subject: [PATCH 003/121] refac: naming --- .../src/composables/useResourceCapabilities.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datagouv-components/src/composables/useResourceCapabilities.ts b/datagouv-components/src/composables/useResourceCapabilities.ts index 14c4f885b..ce911b3b0 100644 --- a/datagouv-components/src/composables/useResourceCapabilities.ts +++ b/datagouv-components/src/composables/useResourceCapabilities.ts @@ -17,7 +17,7 @@ export function useResourceCapabilities( ) { const config = useComponentsConfig() const { t } = useTranslation() - const { hasTabularData: hasTabularDataFromComposable } = useHasTabularData() + const { hasTabularData: checkTabularData } = useHasTabularData() const hasPreview = computed(() => { const format = toValue(resource).format?.toLowerCase() @@ -26,7 +26,7 @@ export function useResourceCapabilities( const hasTabularData = computed(() => { const r = toValue(resource) - return hasTabularDataFromComposable(r) + return checkTabularData(r) }) const hasPmtiles = computed(() => { From 2d187c8e1cf1d3644cc31e5ad4108281e850e761 Mon Sep 17 00:00:00 2001 From: Nicolas Kempf Le Stanc Date: Tue, 14 Apr 2026 18:04:23 +0200 Subject: [PATCH 004/121] wip: add chart visualization types, viewer and configurator --- datagouv-components/package.json | 1 + .../src/components/ChartViewer.vue | 113 +++++++ datagouv-components/src/main.ts | 16 + .../src/types/visualizations.ts | 87 ++++++ design-system/ChartConfigurator.vue | 293 ++++++++++++++++++ nuxt.config.ts | 2 +- pages/design/chart.vue | 22 ++ pnpm-lock.yaml | 34 ++ 8 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 datagouv-components/src/components/ChartViewer.vue create mode 100644 datagouv-components/src/types/visualizations.ts create mode 100644 design-system/ChartConfigurator.vue create mode 100644 pages/design/chart.vue diff --git a/datagouv-components/package.json b/datagouv-components/package.json index 93a85015c..d17ccd248 100644 --- a/datagouv-components/package.json +++ b/datagouv-components/package.json @@ -46,6 +46,7 @@ "@vueuse/router": "^14.2.1", "chart.js": "^4.4.8", "dompurify": "^3.2.5", + "echarts": "^6.0.0", "geopf-extensions-openlayers": "^1.0.0-beta.5", "leaflet": "^1.9.4", "maplibre-gl": "^5.6.2", diff --git a/datagouv-components/src/components/ChartViewer.vue b/datagouv-components/src/components/ChartViewer.vue new file mode 100644 index 000000000..dc30ee4f2 --- /dev/null +++ b/datagouv-components/src/components/ChartViewer.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/datagouv-components/src/main.ts b/datagouv-components/src/main.ts index 39b418ed7..3f5bc9c74 100644 --- a/datagouv-components/src/main.ts +++ b/datagouv-components/src/main.ts @@ -23,6 +23,7 @@ import type { Site } from './types/site' import type { Weight, WellType } from './types/ui' import type { User, UserReference } from './types/users' import type { Report, ReportSubject, ReportReason } from './types/reports' +import type { Chart, ChartForm, FilterCondition, Filter, AndFilters, GenericFilter, XAxisType, XAxisSortBy, SortDirection, XAxis, UnitPosition, YAxis, DataSeriesType, AggregateType, DataSeries } from './types/visualizations' import type { GlobalSearchConfig, SearchType, SortOption } from './types/search' import { getDefaultDatasetConfig, getDefaultDataserviceConfig, getDefaultReuseConfig, getDefaultOrganizationConfig, getDefaultTopicConfig, getDefaultGlobalSearchConfig, defaultDatasetSortOptions, defaultDataserviceSortOptions, defaultReuseSortOptions, defaultOrganizationSortOptions } from './types/search' @@ -216,6 +217,21 @@ export type { ValidataError, Weight, WellType, + Chart, + ChartForm, + FilterCondition, + Filter, + AndFilters, + GenericFilter, + XAxisType, + XAxisSortBy, + SortDirection, + XAxis, + UnitPosition, + YAxis, + DataSeriesType, + AggregateType, + DataSeries, } export { diff --git a/datagouv-components/src/types/visualizations.ts b/datagouv-components/src/types/visualizations.ts new file mode 100644 index 000000000..b24ef4d4c --- /dev/null +++ b/datagouv-components/src/types/visualizations.ts @@ -0,0 +1,87 @@ +import type { Owned, OwnedWithId } from './owned' + +// Filter types +export type FilterCondition = 'equal' | 'greater' + +export type Filter = { + column: string + condition: FilterCondition + value?: string +} + +export type AndFilters = { + filters: Array +} + +export type GenericFilter = Filter | AndFilters + +// Axis types +export type XAxisType = 'discrete' | 'continuous' + +export type XAxisSortBy = 'axis_x' | 'axis_y' + +export type SortDirection = 'asc' | 'desc' + +export type XAxis = { + column_x: string + sort_x_by?: XAxisSortBy + sort_x_direction?: SortDirection + type: XAxisType +} + +export type UnitPosition = 'prefix' | 'suffix' + +export type YAxis = { + min?: number + max?: number + label?: string + unit?: string + unit_position?: UnitPosition +} + +// Data series types +export type DataSeriesType = 'line' | 'histogram' + +export type AggregateType = 'sum' | 'median' + +export type DataSeries = { + type: DataSeriesType + column_y?: string + aggregate_y?: AggregateType + resource_id?: string + column_x_name_override?: string + filters?: GenericFilter +} + +// Chart form fields (for POST/PATCH requests) +export type ChartForm = OwnedWithId & { + title: string + description: string + private?: boolean + x_axis?: XAxis + y_axis?: YAxis + series?: Array + extras?: Record +} + +// Chart read fields (API responses) +export type Chart = Owned & { + id: string + title: string + slug: string + description: string + private: boolean + created_at: string + last_modified: string + deleted_at: string | null + uri: string + page: string + x_axis?: XAxis + y_axis?: YAxis + series?: Array + extras: Record + permissions: { delete: boolean, edit: boolean, read: boolean } + metrics: { + views: number + } +} diff --git a/design-system/ChartConfigurator.vue b/design-system/ChartConfigurator.vue new file mode 100644 index 000000000..27ca2df5a --- /dev/null +++ b/design-system/ChartConfigurator.vue @@ -0,0 +1,293 @@ +