Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
624ae1b
refactor: migrate stored tables to per-data-source schema
nextchamp-saqib Mar 18, 2026
9394324
fix: add debounce
shahzeelahmed Mar 19, 2026
734d522
chore: update POT file (#987)
shahzeelahmed Mar 23, 2026
badce3b
fix: formatting
nextchamp-saqib Mar 23, 2026
0cd29d4
fix: formatting
nextchamp-saqib Mar 23, 2026
7e5ff97
feat: query pagination using offset
nextchamp-saqib Mar 23, 2026
42279da
chore: sync translations from crowdin (#989)
frappe-pr-bot Mar 24, 2026
7854b10
refactor: create pagination composable, split footer component
nextchamp-saqib Mar 24, 2026
273bc15
fix: unable to upload excel/csv files (#964)
shahzeelahmed Mar 24, 2026
e725ef1
fix: resolve fiscal year correctly using fiscal year start setting
shahzeelahmed Mar 24, 2026
674c610
fix: handle fiscal year drilldown filters
shahzeelahmed Mar 24, 2026
2ef6313
Merge pull request #994 fix: misc fiscal year fixes
shahzeelahmed Mar 25, 2026
e88f451
chore: sync translations from crowdin
frappe-pr-bot Mar 25, 2026
691bca2
chore: sync translations from crowdin (#999)
frappe-pr-bot Mar 25, 2026
edad690
fix: remove debounce from emitContent function
nextchamp-saqib Mar 26, 2026
fe6967a
fix: prevent title update on every keystroke
nextchamp-saqib Mar 26, 2026
4609c71
Merge pull request #965 from shahzeelahmed/debounce
nextchamp-saqib Mar 26, 2026
32afa83
fix: ensure `database` exists before `USE` in `WarehouseTableWriter`
nextchamp-saqib Mar 26, 2026
c568360
fix: narrow suppressed exceptions and log failures in warehouse migra…
nextchamp-saqib Mar 26, 2026
fdbb070
Merge pull request #953 from frappe/move-to-schema
nextchamp-saqib Mar 26, 2026
9ae603f
fix: prefix warehouse tables with schema in `get_ibis_table_name` (#1…
nextchamp-saqib Mar 27, 2026
4f94e3d
feat: default filter values in dashboard (#799)
shahzeelahmed Mar 27, 2026
4cc04cd
refactor: replace Autocomplete with Combobox
nextchamp-saqib Mar 27, 2026
290774d
fix: auto match join columns
nextchamp-saqib Mar 27, 2026
2fe4870
fix: improve column resolution with suffix and prefix matching
nextchamp-saqib Mar 27, 2026
7afc024
feat: smarter chart config when source query is summarized
nextchamp-saqib Mar 27, 2026
eadb227
Merge pull request #1004 from nextchamp-saqib/fixes-235115
nextchamp-saqib Mar 28, 2026
765e497
feat: drill-down into source queries
nextchamp-saqib Mar 28, 2026
4f3f774
fix: type error
nextchamp-saqib Mar 28, 2026
0035a13
Merge pull request #1006 from frappe/double-drill-down
nextchamp-saqib Mar 28, 2026
5a2608c
feat: add pivot operation in query builder (#1008)
nextchamp-saqib Mar 28, 2026
6a5b0d6
chore: merge develop
nextchamp-saqib Mar 29, 2026
6b20b3a
chore: merge develop
nextchamp-saqib Mar 29, 2026
6a31a46
refactor: limit -> page size
nextchamp-saqib Mar 29, 2026
713dfa5
feat: server-side pagination with inline column filters
nextchamp-saqib Mar 29, 2026
3cecda0
fix: remove loading bar
nextchamp-saqib Mar 29, 2026
dc48e11
refactor: use watchDebounced for filter changes
nextchamp-saqib Mar 29, 2026
daaa76a
chore: update POT file (#1011)
shahzeelahmed Mar 29, 2026
41d390d
feat: support JSON and JSONL file uploads (#1012)
nextchamp-saqib Mar 29, 2026
96ee8ed
fix: only propagate real totalRowCount from backend, not synthesized …
nextchamp-saqib Mar 29, 2026
5a8ba03
fix: restore apply_limit to honour persisted limit pipeline operations
nextchamp-saqib Mar 29, 2026
2299759
fix: normalize page and page_size params in execute_ibis_query
nextchamp-saqib Mar 29, 2026
ef936c4
refactor: extract `clamp` helper to simplify execute_ibis_query
nextchamp-saqib Mar 29, 2026
390bcf7
fix: simplify using clamp function
nextchamp-saqib Mar 29, 2026
d9b678b
Merge pull request #1010 from frappe/backend-pagination
nextchamp-saqib Mar 29, 2026
8724803
fix: clamp function arguments (#1015)
nextchamp-saqib Mar 29, 2026
3ad473b
fix: add fiscal year labeling to x-axis
Gadha2311 Mar 30, 2026
a5e077a
fix: handle other granulartiy in formatter
shahzeelahmed Mar 30, 2026
39c7653
fix: initialize settings
shahzeelahmed Mar 30, 2026
fe507ca
fix: don't initialize `fiscal_year_start`
shahzeelahmed Mar 30, 2026
3448944
Merge pull request #1017 fix: add fiscal year labeling to x-axis
shahzeelahmed Mar 30, 2026
3eef811
fix: update max page_size (#1020)
nextchamp-saqib Mar 31, 2026
290a0d5
fix: conditionally render prefix and suffix slots (#1023)
nextchamp-saqib Apr 1, 2026
a107db5
fix: add echarts/core to optimizeDeps include list (#1027)
nextchamp-saqib Apr 5, 2026
629edbd
chore: update POT file
frappe-pr-bot Apr 5, 2026
8aaa08e
refactor: remove sass dependency (#752)
ishanloya Apr 6, 2026
d113375
ci(mergify): upgrade configuration to current format (#1018)
mergify[bot] Apr 6, 2026
b03eb3b
refactor: centralize circular query dependency detection (#1032)
nextchamp-saqib Apr 6, 2026
7d1dac5
fix: add connection timeout (#1034)
nextchamp-saqib Apr 6, 2026
0e2a27e
fix: update type hint for test_connection methods to allow None (#1036)
nextchamp-saqib Apr 6, 2026
dff8360
Merge pull request #1029 chore: update POT file
shahzeelahmed Apr 6, 2026
3172b0b
refactor: improve deprecation messaging (#1040)
nextchamp-saqib Apr 7, 2026
2c0dd01
chore: sync translations from crowdin (#1013)
frappe-pr-bot Apr 7, 2026
af3a937
fix: improve error handling and logging in table import log (#1045)
nextchamp-saqib Apr 7, 2026
c9a843d
refactor: update SidebarLink component to use dynamic routing (#1049)
nextchamp-saqib Apr 9, 2026
897efd0
fix: use locale from user (#1051)
nextchamp-saqib Apr 9, 2026
44d5a3e
fix: measure label is reset on blur event (#1053)
nextchamp-saqib Apr 10, 2026
92d81b6
fix: add loading state for data table when loading and not filtering …
nextchamp-saqib Apr 14, 2026
4039cc7
fix: cannot set allowed_directories when external access is disabled …
Copilot Apr 14, 2026
e0ee762
fix: bugfix cursor alignment (#1062)
MarcCon Apr 14, 2026
953f1b3
fix: load datatable when query is loaded
nextchamp-saqib Apr 15, 2026
95f54b8
fix: make `between` default operator
nextchamp-saqib Apr 15, 2026
14b6456
fix: date range filter value
nextchamp-saqib Apr 15, 2026
8c9b99e
fix: various fixes (#1066)
nextchamp-saqib Apr 15, 2026
d20fee1
Merge branch 'version-3-hotfix' into develop
nextchamp-saqib Apr 15, 2026
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
53 changes: 24 additions & 29 deletions .mergify.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
pull_request_rules:
- name: Auto-close PRs on stable branch
conditions:
- name: Auto-close PRs on stable branch
conditions:
- and:
- and:
- and:
- author!=nextchamp-saqib
- author!=shahzeelahmed
- author!=frappe-pr-bot
- author!=mergify[bot]
- or:
- base=main
- base=version-3
actions:
close:
comment:
message: |
@{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on develop branch.
- author!=nextchamp-saqib
- author!=shahzeelahmed
- author!=frappe-pr-bot
- author!=mergify[bot]
- or:
- base=main
- base=version-3
actions:
close:
comment:
message: |
@{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on develop branch.

- name: backport to version-3-hotfix
conditions:
- label="backport version-3-hotfix"
actions:
backport:
branches:
- version-3-hotfix
assignees:
- "{{ author }}"
- name: backport to version-3-hotfix
conditions:
- label="backport version-3-hotfix"
actions:
backport:
branches:
- version-3-hotfix
assignees:
- "{{ author }}"

- name: Auto-delete merged branches
conditions:
- merged
actions:
delete_head_branch:
1 change: 0 additions & 1 deletion frontend/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
declare module 'frappe-ui'
declare module '@/utils/dayjs'
declare module 'dom-to-image'

Expand Down
22 changes: 16 additions & 6 deletions frontend/src2/charts/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,13 @@ function getXAxis(x_axis: XAxis) {
width: 100,
overflow: 'truncate',
ellipsis: '...',
...(x_axis.dimension.granularity === 'fiscal_year'
? {
formatter: (value: any) => {
return getFormattedDate(value, 'fiscal_year')
},
}
: null),
},
}
}
Expand Down Expand Up @@ -502,7 +509,7 @@ export function getDonutChartOptions(config: DonutChartConfig, result: QueryResu
function getDonutChartData(
columns: QueryResultColumn[],
rows: QueryResultRow[],
maxSlices: number
maxSlices: number,
) {
const measureColumn = columns.find((c) => FIELDTYPES.MEASURE.includes(c.type))
if (!measureColumn) {
Expand Down Expand Up @@ -632,7 +639,7 @@ export function getFunnelChartOptions(config: FunnelChartConfig, result: QueryRe
function getMapChartData(
columns: QueryResultColumn[],
rows: QueryResultRow[],
config?: MapChartConfig
config?: MapChartConfig,
) {
const measureColumn = columns.find((c) => FIELDTYPES.MEASURE.includes(c.type))
if (!measureColumn) {
Expand Down Expand Up @@ -888,7 +895,9 @@ export function getBubbleChartOptions(config: BubbleChartConfig, result: QueryRe
// calculate symbol size
let symbolSizeConfig: any = 10
if (sizeColumnName) {
const allSizes = _rows.map((r) => r[sizeColumnName]).filter((val) => val != null && !isNaN(val))
const allSizes = _rows
.map((r) => r[sizeColumnName])
.filter((val) => val != null && !isNaN(val))
if (allSizes.length > 0) {
const minSize = Math.min(...allSizes)
const maxSize = Math.max(...allSizes)
Expand Down Expand Up @@ -1042,16 +1051,17 @@ export function getSankeyChartOptions(config: SankeyChartConfig, result: QueryRe
const sourceColumn = columns.find(
(c) =>
c.name === config.source_column?.dimension_name ||
c.name === config.source_column?.column_name
c.name === config.source_column?.column_name,
)?.name
const targetColumn = columns.find(
(c) =>
c.name === config.target_column?.dimension_name ||
c.name === config.target_column?.column_name
c.name === config.target_column?.column_name,
)?.name
const valueColumn = columns.find(
(c) =>
c.name === config.value_column?.measure_name || c.name === config.value_column?.column_name
c.name === config.value_column?.measure_name ||
c.name === config.value_column?.column_name,
)?.name

if (!sourceColumn || !targetColumn || !valueColumn) {
Expand Down
7 changes: 7 additions & 0 deletions frontend/src2/components/DataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,13 @@ function toggleNewColumn() {
</slot>
</div>

<div
v-else-if="props.loading && !props.filtering"
class="absolute top-10 flex h-[calc(100%-2.5rem)] rounded-b w-full items-center justify-center bg-white/30 backdrop-blur-sm"
>
<LoadingIndicator class="h-5 w-5 text-gray-500" />
</div>

<div v-else class="flex h-full w-full items-center justify-center">
<div class="flex flex-col items-center gap-2">
<Table2Icon class="h-16 w-16 text-gray-300" stroke-width="1.5" />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src2/components/DataTableFooter.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { Button, LoadingIndicator, Tooltip } from 'frappe-ui'
import { ChevronLeft, ChevronRight, RefreshCw } from 'lucide-vue-next'
import { ref } from 'vue'
import type { PaginationState } from '../composables/usePagination'

const props = defineProps<{
Expand Down
29 changes: 22 additions & 7 deletions frontend/src2/dashboard/Filter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const state = reactive(
copy({
operator: filterOperator.value || operatorOptions.value[0].value,
value: filterValue.value,
})
}),
)

const valueSelectorType = computed(() => {
Expand All @@ -51,6 +51,21 @@ function clearFilter() {
filterValue.value = undefined
emit('close')
}

// For a bried period, the value of a date filter with 'between' operator is stored as `from_date,to_date` string. This computed property helps to convert it to array and vice versa for the DateRangePicker component.
const dateRangeVal = computed({
get() {
let val = state.value
if (typeof val === 'string') {
const [from_date, to_date] = val.split(',')
return [from_date, to_date]
}
return val
},
set(val: string) {
state.value = val.split(',')
},
})
</script>

<template>
Expand All @@ -59,7 +74,7 @@ function clearFilter() {
v-if="filterType === 'Number'"
class="w-[200px]"
v-model:operator="state.operator"
v-model:value="(state.value as number)"
v-model:value="state.value as number"
/>
<template v-else>
<div id="operator" class="!min-w-[200px] flex-1">
Expand All @@ -72,27 +87,27 @@ function clearFilter() {
/>
</div>
<div id="value" class="!min-w-[200px] flex-1 flex-shrink-0">
<DatePicker
<DatePicker
v-if="valueSelectorType === 'date'"
:modelValue="state.value as string"
@update:modelValue="state.value = $event"
/>
<DateRangePicker
v-else-if="valueSelectorType === 'date_range'"
v-model="(state.value as string[])"
v-model="dateRangeVal as string[]"
/>
<RelativeDatePicker
v-else-if="valueSelectorType === 'relative_date'"
v-model="(state.value as string)"
v-model="state.value as string"
/>
<ColumnFilterValueSelector
v-else-if="valueSelectorType === 'select'"
v-model="(state.value as string[])"
v-model="state.value as string[]"
:valuesProvider="props.valuesProvider"
/>
<FormControl
v-else-if="valueSelectorType === 'text'"
v-model="state.value"
v-model="state.value as string"
placeholder="Value"
autocomplete="off"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ watchDebounced(
.then((values: string[]) => (distinctColumnValues.value = values))
.finally(() => (fetchingValues.value = false))
},
{ debounce: 300, immediate: true }
{ debounce: 300, immediate: true },
)

const sortedValues = computed(() => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src2/query/components/NativeQueryEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ const completions = computed(() => {
<div class="flex flex-shrink-0 items-center gap-1 border-b p-1">
<DataSourceSelector v-model="data_source" placeholder="Select a data source" />
<ContentEditable
class="flex h-7 cursor-text items-center justify-center rounded bg-white px-2 text-base text-gray-800 focus-visible:ring-1 focus-visible:ring-gray-600"
class="flex h-7 cursor-text items-center justify-center rounded bg-white px-2 text-base leading-7 text-gray-800 focus-visible:ring-1 focus-visible:ring-gray-600"
placeholder="Untitled Dashboard"
:modelValue="query.doc.title"
@returned="query.doc.title = $event"
Expand Down
1 change: 1 addition & 0 deletions frontend/src2/query/components/QueryDataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ function onFilterChange(filters: Record<string, string>) {

<template>
<DataTable
v-if="props.query.isloaded"
:loading="props.query.executing && !isFiltering"
:filtering="props.query.executing && isFiltering"
:columns="columns"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src2/query/components/ScriptQueryEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function handleSaveVariables(variables: any[]) {
<div class="relative flex h-[55%] w-full flex-col rounded border">
<div class="flex flex-shrink-0 items-center gap-1 border-b p-1">
<ContentEditable
class="flex h-7 cursor-text items-center justify-center rounded bg-white px-2 text-base text-gray-800 focus-visible:ring-1 focus-visible:ring-gray-600"
class="flex h-7 cursor-text items-center justify-center rounded bg-white px-2 text-base leading-7 text-gray-800 focus-visible:ring-1 focus-visible:ring-gray-600"
:modelValue="query.doc.title"
@returned="query.doc.title = $event"
@blur="query.doc.title = $event"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src2/query/components/filter_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ export function getOperatorOptions(filterType: FilterType) {
options.push({ label: __('is not set'), value: 'is_not_set' })
}
if (filterType === 'Date') {
options.push({ label: __('between'), value: 'between' })
options.push({ label: __('equals'), value: '=' })
options.push({ label: __('not equals'), value: '!=' })
options.push({ label: __('greater than'), value: '>' })
options.push({ label: __('greater than or equals'), value: '>=' })
options.push({ label: __('less than'), value: '<' })
options.push({ label: __('less than or equals'), value: '<=' })
options.push({ label: __('between'), value: 'between' })
options.push({ label: __('within'), value: 'within' })
options.push({ label: __('is set'), value: 'is_set' })
options.push({ label: __('is not set'), value: 'is_not_set' })
Expand Down
25 changes: 17 additions & 8 deletions frontend/src2/query/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ export function getFormattedRows(result: QueryResult, operations: Operation[]) {
formattedRow[column.name] = getFormattedDate(row[column.name], granularity)
}

if (FIELDTYPES.TEXT.includes(column.type) && typeof row[column.name] === 'string' && row[column.name]) {
if (
FIELDTYPES.TEXT.includes(column.type) &&
typeof row[column.name] === 'string' &&
row[column.name]
) {
const htmlTagRegex = /<[^>]*>/g
if (htmlTagRegex.test(row[column.name])) {
htmlTagRegex.lastIndex = 0
Expand All @@ -146,13 +150,13 @@ export function getFormattedRows(result: QueryResult, operations: Operation[]) {
})
return formattedRows
}

export function getFormattedDate(date: string, granularity: GranularityType) {
export function getFormattedDate(date: string, granularity: string) {
if (!date) return ''
const fy = useSettings()

if (granularity === 'fiscal_year') {
const d = dayjs(date)
const fiscalYearStart = useSettings().doc.fiscal_year_start || '04-01'
const fiscalYearStart = fy.doc.fiscal_year_start
const fiscalStartMonth = dayjs(fiscalYearStart).month()
const fiscalStartDay = dayjs(fiscalYearStart).date()

Expand Down Expand Up @@ -386,7 +390,10 @@ export const query_operation_types = {
icon: Braces,
color: 'gray',
class: 'text-gray-600 bg-gray-100',
init: (args: CustomOperationArgs): CustomOperation => ({ type: 'custom_operation', ...args }),
init: (args: CustomOperationArgs): CustomOperation => ({
type: 'custom_operation',
...args,
}),
getDescription: (op: CustomOperation) => {
return `${op.expression.expression}`
},
Expand All @@ -399,7 +406,7 @@ export const query_operation_types = {
class: 'text-gray-600 bg-gray-100',
init: (args: SQLArgs): SQL => ({ type: 'sql', ...args }),
getDescription: (op: SQL) => {
return __("SQL")
return __('SQL')
},
},
code: {
Expand All @@ -410,7 +417,7 @@ export const query_operation_types = {
class: 'text-gray-600 bg-gray-100',
init: (args: CodeArgs): Code => ({ type: 'code', ...args }),
getDescription: (op: Code) => {
return __("Code")
return __('Code')
},
},
}
Expand Down Expand Up @@ -484,5 +491,7 @@ export function matchesFilter(value: any, parsed: ParsedFilter): boolean {
}
}
// text: case-insensitive substring match
return String(value ?? '').toLowerCase().includes(parsed.text.toLowerCase())
return String(value ?? '')
.toLowerCase()
.includes(parsed.text.toLowerCase())
}
2 changes: 1 addition & 1 deletion frontend/src2/settings/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function makeSettings() {
allowed_origins: '',
max_records_to_sync: 10_00_000,
max_memory_usage: 512,
fiscal_year_start: '2024-04-01',
fiscal_year_start: '',
week_starts_on: 'Monday',
enable_data_store: false,
apply_user_permissions: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# For license information, please see license.txt

import os
from contextlib import suppress
from urllib.parse import urlparse

import frappe
Expand Down Expand Up @@ -32,6 +33,10 @@ def get_local_duckdb_connection(db_name, read_only=True, allowed_dir=None):

if allowed_dir:
escaped_dir = allowed_dir.replace("'", "''")
# Some environments start with external access already disabled.
# Best effort: try enabling it first, then configure directory allowlist.
with suppress(Exception):
db.raw_sql("SET enable_external_access = true")
db.raw_sql(f"SET allowed_directories = ['{escaped_dir}']")

db.raw_sql("SET enable_external_access = false")
Expand Down
Loading
Loading