Skip to content

fix: move null is zero to data source, fix bugs#340

Merged
joaquimds merged 8 commits intomainfrom
feat/fix-null-is-zero-issues
Feb 26, 2026
Merged

fix: move null is zero to data source, fix bugs#340
joaquimds merged 8 commits intomainfrom
feat/fix-null-is-zero-issues

Conversation

@joaquimds
Copy link
Member

No description provided.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR moves the “treat empty values as zero” behavior from per-map-view configuration to the DataSource model/config, and updates stats calculation + UI to reflect the new source of truth.

Changes:

  • Persist nullIsZero on DataSource (schema + migration) and allow updating it via dataSource.updateConfig.
  • Remove nullIsZero/areaDataNullIsZero from area stats inputs and map view config, and update area stats computation to use dataSource.nullIsZero.
  • Update UI: remove the per-view toggle, add a DataSource configuration toggle, and improve Legend label rendering with value labels.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/server/trpc/routers/dataSource.ts Includes nullIsZero in DataSource config updates.
src/server/trpc/routers/area.ts Removes nullIsZero from the area stats input.
src/server/stats/index.ts Stats now use dataSource.nullIsZero (and adjust MODE selection).
src/server/models/MapView.ts Removes areaDataNullIsZero from persisted view config schema.
src/server/models/DataSource.ts Adds nullIsZero to the DataSource Zod schema/type.
src/app/map/[id]/utils/mapView.ts Removes default areaDataNullIsZero from new view configs.
src/app/map/[id]/data.ts Stops sending nullIsZero to the stats query and removes it from reset deps.
src/app/map/[id]/components/controls/VisualisationPanel/VisualisationPanel.tsx Removes the per-view “Treat empty values as zero” switch; minor className update.
src/app/map/[id]/components/Legend.tsx Uses valueLabels for legend labels; adjusts tick generation based on labels.
src/app/(private)/data-sources/[id]/components/ConfigurationForm.tsx Adds a DataSource-level “Treat empty values as zero” switch and includes it in updates.
migrations/1772117945260_data_source_null_is_zero.ts Adds nullIsZero column to dataSource.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 310 to 317
if (hasValueLabels) {
numTicks = Object.keys(valueLabels).length;
values = Object.keys(valueLabels)
.map(Number)
.filter((v) => v >= colorScheme.minValue && v <= colorScheme.maxValue)
.toSorted();
numTicks = values.length;
denom = Math.max(numTicks - 1, 1);
values = Object.keys(valueLabels).map(Number).toSorted();
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When hasValueLabels is true, keys are filtered to those within [minValue, maxValue]. If none of the configured labels fall in-range, this sets numTicks to 0 and renders no tick labels (but still reserves the taller h-10 layout). Consider falling back to the default tick generation when the filtered list is empty (or at least keeping a minimum of 2 ticks).

Copilot uses AI. Check for mistakes.
Comment on lines 239 to 243
dateFormat: input.dateFormat,
public: input.public,
naIsNull: input.naIsNull,
nullIsZero: input.nullIsZero,
} as DataSourceUpdate;
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new nullIsZero flag is persisted via updateConfig, but the mutation handler only enqueues an importDataSource job when certain config fields change. If naIsNull (and possibly nullIsZero, depending on intent) should take effect on existing imported records, it likely needs to be included in the “configFieldsChanged” check; otherwise the UI’s “Save and import” flow won’t actually import when toggling these flags.

Copilot uses AI. Check for mistakes.
Comment on lines 107 to 116
updateDataSourceConfig({
dataSourceId: dataSource.id,
columnRoles,
geocodingConfig: validGeocodingConfig,
autoImport,
dateFormat,
public: isPublic,
naIsNull,
nullIsZero,
});
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After saving nullIsZero, the client only invalidates dataSource.checkWebhookStatus. Since map rendering/stats now depend on dataSource.nullIsZero, consider also invalidating cached data source queries (e.g. dataSource.byId, dataSource.listReadable) and/or area.stats so other parts of the app don’t keep using stale cached results.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment on lines 48 to 55
const {
calculationType,
areaDataColumn: column,
areaDataSecondaryColumn: secondaryColumn,
areaDataSourceId: dataSourceId,
areaDataNullIsZero: nullIsZero,
areaSetGroupCode,
} = viewConfig;

Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullIsZero no longer participates in the area.stats query input/query key. Since the server-side stats result now depends on dataSource.nullIsZero, React Query can keep serving cached stats after the flag changes (e.g., user updates the data source config in another tab) until a manual invalidation/refetch happens. Consider invalidating trpc.area.stats queries when dataSource.updateConfig succeeds, or include a stable dataSource config version (or nullIsZero) in the query key/reset key so stats recompute immediately after this setting changes.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI commented Feb 26, 2026

@joaquimds I've opened a new pull request, #341, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 2 commits February 26, 2026 15:51
…onfig save

Co-authored-by: joaquimds <12935136+joaquimds@users.noreply.github.com>
Invalidate additional caches after data source config save
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +271 to +275
// Coalesce mode empty values to 0 if the column is numeric and nullIsZero is set
const modeSelect =
columnDef.type === ColumnType.Number && dataSource.nullIsZero
? sql`MODE () WITHIN GROUP (ORDER BY COALESCE(NULLIF(json->>${column}, ''), '0'))`
: sql`MODE () WITHIN GROUP (ORDER BY NULLIF(json->>${column}, ''))`;
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new MODE query path also changes behavior when nullIsZero is enabled (and now treats empty strings as NULL when disabled). Please add a focused test for CalculationType.Mode on a numeric column with blank values to lock in the intended behavior for both nullIsZero=true and nullIsZero=false.

Copilot uses AI. Check for mistakes.
Comment on lines +266 to 269
// Coalesce numeric empty values to 0 if set
const numberSelect = dataSource.nullIsZero
? sql`(COALESCE(NULLIF(json->>${column}, ''), '0'))::float`
: sql`(NULLIF(json->>${column}, ''))::float`;
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getColumnValueByArea now changes numeric aggregation semantics based on dataSource.nullIsZero (coalescing empty strings to 0). There are existing Vitest feature tests that exercise getAreaStats, but none cover the new nullIsZero behavior; please add a test case that imports a CSV with blank numeric values and asserts that Sum/Avg differ when nullIsZero is enabled vs disabled.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

>
<SelectTrigger
className="w-full min-w-0"
className={cn("w-full min-w-0", SELECT_TO_BUTTON_CLASSES)}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant SELECT_TO_BUTTON_CLASSES is referenced but not visible in the diff. Ensure this constant is properly imported or defined in this file.

Copilot uses AI. Check for mistakes.
joaquimds and others added 2 commits February 26, 2026 19:12
@joaquimds joaquimds merged commit e54925d into main Feb 26, 2026
1 check passed
@joaquimds joaquimds deleted the feat/fix-null-is-zero-issues branch February 26, 2026 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants