diff --git a/app/components/form.tsx b/app/components/form.tsx index e5174e4f7d..bdb15097e2 100644 --- a/app/components/form.tsx +++ b/app/components/form.tsx @@ -157,12 +157,14 @@ export const Select = ({ options, onChange, sortOptions = true, + controls, }: { id: string; options: Option[]; label?: ReactNode; disabled?: boolean; sortOptions?: boolean; + controls?: React.ReactNode; } & SelectProps) => { const locale = useLocale(); const sortedOptions = useMemo(() => { @@ -181,6 +183,7 @@ export const Select = ({ {label && ( )} void; }) => { + const controls = ( + <> + + + + ); return ( {dimension.__typename === "TemporalDimension" ? ( { + const keys = Object.keys(chartConfig.filters); + const fieldIndex = Object.keys(chartConfig.filters).indexOf(dimensionIri); + if (fieldIndex == 0 && delta === -1) { + return; + } + if (fieldIndex === keys.length - 1 && delta === 1) { + return; + } + const replaced = keys[fieldIndex + delta]; + keys[fieldIndex + delta] = dimensionIri; + keys[fieldIndex] = replaced; + chartConfig.filters = Object.fromEntries( + keys.map((k) => [k, chartConfig.filters[k]]) + ); + } +); + +const ensureFilterValuesCorrect = produce( + ( + chartConfig: ChartConfig, + { dimensions }: { dimensions: DataCubeMetadata["dimensions"] } + ) => { + let dirty = false; + const newFilters = mapValues(chartConfig.filters, (f, dimensionIri) => { + if (f.type !== "single") { + return f; + } + const values = dimensions.find((dim) => dim.iri === dimensionIri)?.values; + if (!values || values.length === 0) { + return f; + } + if (values.find((v) => v.value === f.value)) { + return f; + } + dirty = true; + f.value = values[0].value; + return f; + }); + if (dirty) { + chartConfig.filters = newFilters; + } + } +); + export const ChartConfigurator = ({ state, }: { state: ConfiguratorStateConfiguringChart; }) => { const locale = useLocale(); - const [{ data }] = useDataCubeMetadataWithComponentValuesQuery({ - variables: { + const [, dispatch] = useConfiguratorState(); + const variables = React.useMemo( + () => ({ + date: new Date(), iri: state.dataSet, locale, filters: state.chartConfig.filters, + }), + [state, locale] + ); + const [{ data, fetching }, executeQuery] = + useDataCubeMetadataWithComponentValuesQuery({ + variables: variables, + }); + const metaData = data?.dataCubeByIri; + + const handleMove = useCallback( + (dimensionIri: string, delta: number) => { + if (!metaData) { + return; + } + + const chartConfig = moveFilterField(state.chartConfig, { + dimensionIri, + delta, + }); + + dispatch({ + type: "CHART_CONFIG_REPLACED", + value: { + chartConfig, + dataSetMetadata: metaData, + }, + }); }, - }); + [dispatch, metaData, state.chartConfig] + ); + + React.useEffect(() => { + executeQuery({ + requestPolicy: "network-only", + variables, + }); + }, [variables, executeQuery]); + + React.useEffect(() => { + if (!metaData || !data || !data.dataCubeByIri) { + return; + } + // Make sure that the filters are in line with the values + const chartConfig = ensureFilterValuesCorrect(state.chartConfig, { + dimensions: data.dataCubeByIri.dimensions, + }); + + dispatch({ + type: "CHART_CONFIG_REPLACED", + value: { + chartConfig, + dataSetMetadata: metaData, + }, + }); + }, [data, dispatch, metaData, state.chartConfig]); if (data?.dataCubeByIri) { const mappedIris = getFieldComponentIris(state.chartConfig.fields); - const filterDimensions = data?.dataCubeByIri.dimensions - .filter((dim) => !mappedIris.has(dim.iri)) - .sort((a, b) => (a.isKeyDimension ? 0 : 1)); - + const keysOrder = Object.fromEntries( + Object.keys(state.chartConfig.filters).map((k, i) => [k, i]) + ); + const filterDimensions = sortBy( + data?.dataCubeByIri.dimensions.filter((dim) => !mappedIris.has(dim.iri)), + [(x) => keysOrder[x.iri] ?? Infinity] + ); return ( <> @@ -96,6 +237,7 @@ export const ChartConfigurator = ({ Filters + {fetching ? "..." : ""} {filterDimensions.map((dimension, i) => ( @@ -103,6 +245,7 @@ export const ChartConfigurator = ({ key={dimension.iri} dimension={dimension} index={i} + onMove={(n) => handleMove(dimension.iri, n)} /> ))} diff --git a/app/configurator/components/field.tsx b/app/configurator/components/field.tsx index 0bba025b7c..e7d7b2d335 100644 --- a/app/configurator/components/field.tsx +++ b/app/configurator/components/field.tsx @@ -67,6 +67,7 @@ export const DataFilterSelect = ({ id, disabled, isOptional, + controls, }: { dimensionIri: string; label: string; @@ -74,6 +75,7 @@ export const DataFilterSelect = ({ id: string; disabled?: boolean; isOptional?: boolean; + controls: React.ReactNode; }) => { const fieldProps = useSingleFilterSelect({ dimensionIri }); @@ -106,6 +108,7 @@ export const DataFilterSelect = ({ label={isOptional ? `${label} (${optionalLabel})` : label} disabled={disabled} options={allOptions} + controls={controls} {...fieldProps} > ); @@ -121,6 +124,7 @@ export const DataFilterSelectTime = ({ id, disabled, isOptional, + controls, }: { dimensionIri: string; label: string; @@ -131,6 +135,7 @@ export const DataFilterSelectTime = ({ id: string; disabled?: boolean; isOptional?: boolean; + controls?: React.ReactNode; }) => { const fieldProps = useSingleFilterSelect({ dimensionIri }); const formatLocale = useTimeFormatLocale(); @@ -184,6 +189,7 @@ export const DataFilterSelectTime = ({ disabled={disabled} options={allOptions} sortOptions={false} + controls={controls} {...fieldProps} > ); diff --git a/app/pages/api/graphql.ts b/app/pages/api/graphql.ts index d08630b9e0..9b7b1f1fae 100644 --- a/app/pages/api/graphql.ts +++ b/app/pages/api/graphql.ts @@ -3,7 +3,6 @@ import configureCors from "cors"; import DataLoader from "dataloader"; import "global-agent/bootstrap"; import { NextApiRequest, NextApiResponse } from "next"; -import { Filters } from "../../configurator"; import { resolvers } from "../../graphql/resolvers"; import typeDefs from "../../graphql/schema.graphql"; import { runMiddleware } from "../../lib/run-middleware"; diff --git a/app/rdf/query-dimension-values.ts b/app/rdf/query-dimension-values.ts index aa0a80fbe9..ef53dffcb1 100644 --- a/app/rdf/query-dimension-values.ts +++ b/app/rdf/query-dimension-values.ts @@ -81,12 +81,14 @@ export async function loadDimensionValues( ): Promise> { const dimensionIri = dimension.path; - let filterList = filters ? Object.entries(filters) : []; - filterList = filterList.slice( + let allFiltersList = filters ? Object.entries(filters) : []; + const filterList = allFiltersList.slice( 0, - filterList.findIndex(([iri]) => iri == dimensionIri?.value) + 1 + allFiltersList.findIndex(([iri]) => iri == dimensionIri?.value) ); + console.log("filters for ", dimensionIri?.value, allFiltersList, filterList); + let query = SELECT.DISTINCT`?value`.WHERE` ${datasetIri} ${cubeNs.observationSet} ?observationSet . ?observationSet ${cubeNs.observation} ?observation .