Skip to content
Open
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
996 changes: 611 additions & 385 deletions components/Charts/ChartConfigurator.vue

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion components/ProducerSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
color="primary"
@click="navigateTo('/admin/organizations/new/')"
>
{{ t("Créer ou rejoindre une organisation") }}
<span class="block whitespace-normal">{{ t("Créer ou rejoindre une organisation") }}</span>
</BrandedButton>
</PaddedContainer>
</div>
Expand Down
19 changes: 10 additions & 9 deletions datagouv-components/src/components/Chart/ChartViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const props = defineProps<{
chart: Chart | ChartForApi
series: {
data: Record<string, Array<Record<string, unknown>>>
columns: Record<string, Array<string>>
columns: Record<string, Array<{ name: string, type: string }>>
}
}>()

Expand All @@ -32,7 +32,7 @@ let echartsInstance: EChartsType | null = null

function mapXAxisType(xAxis: XAxis | XAxisForm): 'category' | 'value' {
if (!xAxis) return 'category'
return (xAxis.type ?? 'discrete') === 'continuous' ? 'value' : 'category'
return xAxis.type === 'continuous' ? 'value' : 'category'
}

function buildYAxisFormatter(yAxis: YAxis): ((value: number) => string) | undefined {
Expand All @@ -53,7 +53,8 @@ const echartsOption = computed(() => {
const yColumn = s.aggregate_y ? `${s.column_y}__${s.aggregate_y}` : s.column_y
const resourceId = s.resource_id

if (!xColumn || !yColumn || !resourceId || !s.type || !props.series.data[resourceId] || !props.series.columns[resourceId]) {
const resourceColumns = props.series.columns[resourceId]
if (!xColumn || !yColumn || !resourceId || !s.type || !props.series.data[resourceId] || !resourceColumns) {
return null
}

Expand Down Expand Up @@ -110,7 +111,7 @@ const echartsOption = computed(() => {
return {
series: {
type: s.type === 'histogram' ? 'bar' : 'line',
dimensions: s.aggregate_y ? [xColumn, yColumn] : props.series.columns[resourceId],
dimensions: s.aggregate_y ? [xColumn, yColumn] : resourceColumns.map(c => c.name),
name: yColumn,
encode: {
x: xColumn,
Expand All @@ -119,7 +120,7 @@ const echartsOption = computed(() => {
} as LineSeriesOption | BarSeriesOption,
data: {
source: sortedData,
dimensions: s.aggregate_y ? [xColumn, yColumn] : props.series.columns[resourceId],
dimensions: s.aggregate_y ? [xColumn, yColumn] : resourceColumns.map(c => c.name),
},
}
})
Expand All @@ -137,10 +138,10 @@ const echartsOption = computed(() => {

return {
dataset: [...seriesData.data],
title: {
text: props.chart.title,
left: 'center',
textStyle: {
fontFamily: 'Marianne, arial, sans-serif',
},
color: '#000091',
tooltip: {
trigger: 'axis' as const,
formatter: (params: Array<{ value: Record<string, unknown>, axisValueLabel: string, seriesName: string }>) => {
Expand All @@ -154,7 +155,7 @@ const echartsOption = computed(() => {
},
},
legend: {
show: seriesData.series.length > 1,
show: false,
bottom: 0,
},
grid: {
Expand Down
34 changes: 26 additions & 8 deletions datagouv-components/src/components/Chart/ChartViewerWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,23 @@
</template>

<script setup lang="ts">
import { reactive, ref, watch } from 'vue'
import { computed, reactive, ref, watch } from 'vue'
import ChartViewer from './ChartViewer.vue'
import LoadingBlock from '../../components/LoadingBlock.vue'
import { useComponentsConfig } from '../../config'
import { fetchTabularData, useGetProfile } from '../../functions/tabularApi'
import type { Chart, ChartForApi } from '../../types/visualizations'
import { useTranslation } from '../../composables/useTranslation'
import { watchDebounced } from '@vueuse/shared'
import { resolveColumnType } from '../../functions/tabular'
import type { ColumnType } from '../TabularExplorer/types'

const props = defineProps<{
chart: Chart | ChartForApi
}>()

const emit = defineEmits<{
columns: [columns: Record<string, Array<string>>]
columns: [columns: Record<string, Array<{ name: string, type: ColumnType }>>]
}>()

const { t } = useTranslation()
Expand All @@ -45,12 +48,18 @@ const pendingOperations = ref(0)

const series = reactive<{
data: Record<string, Array<Record<string, unknown>>>
columns: Record<string, Array<string>>
columns: Record<string, Array<{ name: string, type: ColumnType }>>
}>({
data: {},
columns: {},
})

const resourceIds = computed(() => {
return props.chart.series
.filter(s => s.resource_id)
.map(s => s.resource_id)
})

async function fetchSeriesProfile() {
pendingOperations.value++
status.value = 'pending'
Expand Down Expand Up @@ -78,14 +87,20 @@ async function fetchSeriesProfile() {
.map(r => r.value)
series.columns = Object.fromEntries(results.map(result => [
result.id,
result.profile.profile.header,
result.profile.profile.header.map((name) => {
const colInfo = result.profile.profile.columns[name]
const isCategorical = result.profile.profile.categorical.includes(name)
const colType = resolveColumnType(colInfo ?? { python_type: 'unknown', format: undefined }, isCategorical)
return { name, type: colType }
}),
]))
}
catch (err) {
error.value = err instanceof Error ? err : new Error('Failed to fetch series profile')
status.value = 'error'
console.error(err)
series.columns = {}
series.data = {}
}
finally {
pendingOperations.value--
Expand Down Expand Up @@ -148,13 +163,16 @@ async function fetchSeriesData() {
}
}

watch(() => props.chart.series, async () => {
watch(resourceIds, async () => {
await fetchSeriesProfile()
}, { immediate: true, deep: true })
}, { immediate: true })

watch([() => props.chart.series, () => props.chart.x_axis.column_x], async () => {
watchDebounced([
() => props.chart.series,
() => props.chart.x_axis.column_x,
], async () => {
await fetchSeriesData()
}, { immediate: true, deep: true })
}, { debounce: config.searchDebounce ?? 300, immediate: true, deep: true })

watch(() => series.columns, () => {
emit('columns', series.columns)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ import {
import { useFetch } from '../../functions/api'
import { useComponentsConfig } from '../../config'
import { useTranslation } from '../../composables/useTranslation'
import { buildTypeConfig, hasFilterForColumn as _hasFilterForColumn, isTruthy, isFalsy } from '../../functions/tabular'
import { buildTypeConfig, hasFilterForColumn as _hasFilterForColumn, isTruthy, isFalsy, resolveColumnType } from '../../functions/tabular'
import ClientOnly from '../ClientOnly.vue'
import SimpleBanner from '../SimpleBanner.vue'
import BrandedButton from '../BrandedButton.vue'
Expand Down Expand Up @@ -770,23 +770,18 @@ function toggleMobileFilterColumn(col: string) {
}

// Column type helpers
function resolveColumnType(col: string): ColumnType {
function getColumnTypeFromName(col: string): ColumnType {
const profile = profileData.value?.profile
if (!profile) return 'text'
const colInfo = profile.columns[col]
if (!colInfo) return 'text'
if (['int', 'float'].includes(colInfo.python_type)) return 'number'
if (colInfo.format === 'year') return 'date'
if (['date', 'datetime'].includes(colInfo.python_type)) return 'date'
if (colInfo.python_type === 'bool') return 'boolean'
if (profile.categorical.includes(col)) return 'categorical'
return 'text'
return resolveColumnType(colInfo, profile.categorical.includes(col))
}

const columnTypesMap = computed(() => {
const map: Record<string, ColumnType> = {}
for (const col of allColumns.value) {
map[col] = resolveColumnType(col)
map[col] = getColumnTypeFromName(col)
}
return map
})
Expand Down
9 changes: 9 additions & 0 deletions datagouv-components/src/functions/tabular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ export function buildTypeConfig(t: (s: string) => string): Record<ColumnType, {
}
}

export function resolveColumnType(colInfo: { python_type: string, format?: string }, isCategorical: boolean): ColumnType {
if (['int', 'float'].includes(colInfo.python_type)) return 'number'
if (colInfo.format === 'year') return 'date'
if (['date', 'datetime'].includes(colInfo.python_type)) return 'date'
if (colInfo.python_type === 'bool') return 'boolean'
if (isCategorical) return 'categorical'
return 'text'
}

export function useFormatTabular() {
const { locale } = useTranslation()

Expand Down
2 changes: 2 additions & 0 deletions datagouv-components/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ 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, ChartForApi, FilterCondition, Filter, AndFilters, GenericFilter, XAxisType, XAxisSortBy, SortDirection, XAxis, XAxisForm, UnitPosition, YAxis, DataSeriesType, DataSeries, DataSeriesForm } from './types/visualizations'
import type { ColumnType } from './components/TabularExplorer/types'
import type { GlobalSearchConfig, SearchType, SearchTypeConfig, SortOption, HiddenFilter, BuiltInFilterKey, DatasetSearchConfig, DatasetSearchFilters, DataserviceSearchConfig, DataserviceSearchFilters, ReuseSearchConfig, ReuseSearchFilters, OrganizationSearchConfig, OrganizationSearchFilters, TopicSearchConfig, TopicSearchFilters } from './types/search'
import { getDefaultDatasetConfig, getDefaultDataserviceConfig, getDefaultReuseConfig, getDefaultOrganizationConfig, getDefaultTopicConfig, getDefaultGlobalSearchConfig, defaultDatasetSortOptions, defaultDataserviceSortOptions, defaultReuseSortOptions, defaultOrganizationSortOptions } from './types/search'
import { useSearchFilter } from './composables/useSearchFilter'
Expand Down Expand Up @@ -247,6 +248,7 @@ export type {
Chart,
ChartForm,
ChartForApi,
ColumnType,
FilterCondition,
Filter,
AndFilters,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"lint:fix": "eslint . --fix",
"lint-staged": "lint-staged",
"build": "NODE_OPTIONS=--max-old-space-size=8192 nuxt build",
"typecheck": "nuxt typecheck",
"typecheck": "NODE_OPTIONS=--max-old-space-size=8192 nuxt typecheck",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
Expand Down
19 changes: 9 additions & 10 deletions pages/design/chart.vue → pages/admin/beta/chart.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
<template>
<div>
<Breadcrumb>
<BreadcrumbItem to="/design">
{{ $t('Système de design') }}
<BreadcrumbItem to="/">
{{ $t('Accueil') }}
</BreadcrumbItem>
<BreadcrumbItem to="/design/chart">
{{ $t('Chart') }}
<BreadcrumbItem>
{{ $t('Visualisations') }}
</BreadcrumbItem>
<BreadcrumbItem>
{{ $t('Formulaire de publication') }}
</BreadcrumbItem>
</Breadcrumb>
<h1 class="mb-3">
Chart Configurator
</h1>

<ChartConfigurator v-model="chart" />
</div>
</template>
Expand All @@ -21,12 +20,12 @@ import type { ChartForm } from '@datagouv/components-next'
import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
import ChartConfigurator from '~/components/Charts/ChartConfigurator.vue'

const me = useMaybeMe()
const me = useMe()

const chart = ref<ChartForm>({
owned: {
organization: null,
owner: me.value ? me.value.id : 'dummyForDS',
owner: me.value.id,
},
title: 'Mon graphique',
description: '',
Expand Down
File renamed without changes
Loading
Loading