Skip to content

Commit

Permalink
Merge remote-tracking branch 'AlejandroCano/CustomDrilldowns' into Cu…
Browse files Browse the repository at this point in the history
…stomDrilldown

# Conflicts:
#	Signum.React/Scripts/SearchControl/SearchControlLoaded.tsx
  • Loading branch information
olmobrutall committed Feb 6, 2023
2 parents c681882 + 911be53 commit 75c713f
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 89 deletions.
1 change: 0 additions & 1 deletion Signum.React.Extensions/Chart/ChartClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,6 @@ export module Encoder {
return p.element.value != defaultParameterValue(scriptParam, c?.token && c.token.token);
})
.map(p => ({ name: p.element.name, value: p.element.value }) as ChartParameterOption),
customDrilldowns: cr.customDrilldowns,
};
}

Expand Down
10 changes: 6 additions & 4 deletions Signum.React.Extensions/Chart/Templates/ChartRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DomUtils, Dic } from '@framework/Globals'
import * as Finder from '@framework/Finder'
import * as Navigator from '@framework/Navigator'
import { FilterOptionParsed, ColumnOption, hasAggregate, withoutAggregate, FilterOption, FindOptions, withoutPinned } from '@framework/FindOptions'
import { ChartRequestModel, ChartMessage } from '../Signum.Entities.Chart'
import { ChartRequestModel, ChartMessage, UserChartEntity } from '../Signum.Entities.Chart'
import * as ChartClient from '../ChartClient'
import { toFilterOptions } from '@framework/Finder';

Expand All @@ -19,8 +19,10 @@ import { toAbsoluteUrl } from '@framework/AppContext'
import * as UserQueryClient from '../../UserQueries/UserQueryClient'
import { DynamicTypeConditionSymbolEntity } from '../../Dynamic/Signum.Entities.Dynamic'
import { extractFindOptions } from '../../UserQueries/UserQueryClient'
import { Lite } from '@framework/Signum.Entities'

export interface ChartRendererProps {
userChart?: Lite<UserChartEntity>;
chartRequest: ChartRequestModel;
loading: boolean;

Expand Down Expand Up @@ -56,7 +58,7 @@ export default function ChartRenderer(p: ChartRendererProps) {
data={p.data}
dashboardFilter={p.dashboardFilter}
loading={p.loading}
onDrillDown={p.onDrillDown ?? ((r, e) => handleDrillDown(r, e, p.lastChartRequest!, p.autoRefresh ? p.onReload : undefined))}
onDrillDown={p.onDrillDown ?? ((r, e) => handleDrillDown(r, e, p.lastChartRequest!, p.userChart, p.autoRefresh ? p.onReload : undefined))}
onBackgroundClick={p.onBackgroundClick}
parameters={parameters}
onReload={p.onReload}
Expand All @@ -69,12 +71,12 @@ export default function ChartRenderer(p: ChartRendererProps) {
);
}

export function handleDrillDown(r: ChartRow, e: React.MouseEvent | MouseEvent, cr: ChartRequestModel, onReload?: () => void) {
export function handleDrillDown(r: ChartRow, e: React.MouseEvent | MouseEvent, cr: ChartRequestModel, uc?: Lite<UserChartEntity>, onReload?: () => void) {

e.stopPropagation();
var newWindow = e.ctrlKey || e.button == 1;

UserQueryClient.onDrilldownUserChart(cr, r, { openInNewTab: newWindow, onReload })
UserQueryClient.onDrilldownUserChart(cr, r, uc, { openInNewTab: newWindow, onReload })
.then(done => {
if (done == false) {
if (r.entity) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DomUtils, Dic } from '@framework/Globals'
import * as Finder from '@framework/Finder'
import * as Navigator from '@framework/Navigator'
import { FilterOptionParsed, ColumnOption, hasAggregate, withoutAggregate, FilterOption, FindOptions, withoutPinned } from '@framework/FindOptions'
import { ChartRequestModel, ChartMessage } from '../Signum.Entities.Chart'
import { ChartRequestModel, ChartMessage, UserChartEntity } from '../Signum.Entities.Chart'
import * as ChartClient from '../ChartClient'
import { toFilterOptions } from '@framework/Finder';

Expand All @@ -17,6 +17,7 @@ import { TypeInfo } from '@framework/Reflection'
import { FullscreenComponent } from './FullscreenComponent'
import { handleDrillDown } from './ChartRenderer'
import { ReactChartCombined } from '../D3Scripts/Components/ReactChartCombined'
import { Lite } from '@framework/Signum.Entities'



Expand All @@ -28,6 +29,7 @@ export interface ChartRendererCombinedProps {
}

export interface ChartRendererCombinedInfo {
userChart: Lite<UserChartEntity>;
chartRequest: ChartRequestModel;
chartScript: ChartScript;
data?: ChartClient.ChartTable;
Expand All @@ -41,7 +43,7 @@ export default function ChartRendererCombined(p: ChartRendererCombinedProps) {
<ErrorBoundary deps={p.infos.map(a => a.data)}>
<ReactChartCombined useSameScale={p.useSameScale} minHeigh={p.minHeigh} infos={p.infos.map(info => ({
chartRequest: info.chartRequest,
onDrillDown: (r, e) => handleDrillDown(r, e, info.chartRequest),
onDrillDown: (r, e) => handleDrillDown(r, e, info.chartRequest, info.userChart),
parameters: ChartClient.API.getParameterWithDefault(info.chartRequest, info.chartScript),
data: info.data,
memo: info.memo
Expand Down
21 changes: 1 addition & 20 deletions Signum.React.Extensions/Chart/Templates/ChartRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import { ChartScript, cleanedChartRequest, getCustomDrilldownsFindOptions, hasAg
import { useForceUpdate, useAPI } from '@framework/Hooks'
import { AutoFocus } from '@framework/Components/AutoFocus';
import PinnedFilterBuilder from '@framework/SearchControl/PinnedFilterBuilder';
import { EntityStrip } from '../../../Signum.React/Scripts/Lines';

interface ChartRequestViewProps {
chartRequest: ChartRequestModel;
Expand Down Expand Up @@ -58,19 +57,6 @@ export default function ChartRequestView(p: ChartRequestViewProps) {
loading: boolean;
} | undefined>(undefined);

const hasAggregatesRef = React.useRef<boolean>(hasAggregates(p.chartRequest));

React.useEffect(() => {
const ha = hasAggregates(p.chartRequest);
if (ha == hasAggregatesRef.current)
return;

hasAggregatesRef.current = ha;
p.chartRequest.customDrilldowns = [];
p.chartRequest.modified = true;
forceUpdate();
});

const queryDescription = useAPI(signal => p.chartRequest ? Finder.getQueryDescription(p.chartRequest.queryKey) : Promise.resolve(undefined),
[p.chartRequest.queryKey]);

Expand Down Expand Up @@ -215,11 +201,6 @@ export default function ChartRequestView(p: ChartRequestViewProps) {
forceUpdate();
}}
/>
<EntityStrip ctx={tc.subCtx(e => e.customDrilldowns)}
findOptions={getCustomDrilldownsFindOptions(p.chartRequest.queryKey, qd, hasAggregatesRef.current)}
avoidDuplicates={true}
vertical={true}
iconStart={true} />
</>}
</div>
<div className="sf-query-button-bar btn-toolbar mb-2">
Expand All @@ -239,7 +220,7 @@ export default function ChartRequestView(p: ChartRequestViewProps) {
<div className="sf-chart-tab-container">
<Tabs id="chartResultTabs" key={showChartSettings + ""}>
<Tab eventKey="chart" title={ChartMessage.Chart.niceToString()}>
<ChartRenderer chartRequest={cr} loading={loading == true} autoRefresh={false} lastChartRequest={result?.lastChartRequest} data={result?.chartResult.chartTable} minHeight={null} />
<ChartRenderer userChart={p.userChart} chartRequest={cr} loading={loading == true} autoRefresh={false} lastChartRequest={result?.lastChartRequest} data={result?.chartResult.chartTable} minHeight={null} />
</Tab>
{result &&
<Tab eventKey="data" title={<span>{ChartMessage.Data.niceToString()} (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ export module Converter {
export async function applyUserChart(cr: ChartRequestModel, uc: UserChartEntity, entity?: Lite<Entity>): Promise<ChartRequestModel> {
cr.chartScript = uc.chartScript;
cr.maxRows = uc.maxRows;
cr.customDrilldowns = uc.customDrilldowns;

const filters = await UserAssetsClient.API.parseFilters({
queryKey: uc.query.key,
Expand Down
1 change: 0 additions & 1 deletion Signum.React.Extensions/Chart/UserChart/UserChartMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ export default function UserChartMenu(p: UserChartMenuProps) {
filters: qfs.map(f => newMListElement(UserAssetClient.Converter.toQueryFilterEmbedded(f))),
columns: cr.columns.map(a => newMListElement(JSON.parse(JSON.stringify(a.element)))),
parameters: cr.parameters.map(p => newMListElement(JSON.parse(JSON.stringify(p.element)))),
customDrilldowns: cr.customDrilldowns.map(p => newMListElement(JSON.parse(JSON.stringify(p.element)))),
});

return uc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export default function CombinedUserChartPart(p: PanelPartContentProps<CombinedU
onReload={e => { e.preventDefault(); c.makeQuery!(); }}
/>) :
<ChartRendererCombined
infos={infos.map(c => ({ chartRequest: c.chartRequest!, data: c.result?.chartTable, chartScript: c.chartScript!, memo: c.memo }))}
infos={infos.map(c => ({ userChart: toLite(c.userChart, true), chartRequest: c.chartRequest!, data: c.result?.chartTable, chartScript: c.chartScript!, memo: c.memo }))}
onReload={e => { infos.forEach(a => a.makeQuery!()) }}
useSameScale={p.content.useSameScale}
minHeigh={p.content.minHeight}
Expand Down
8 changes: 5 additions & 3 deletions Signum.React.Extensions/Dashboard/View/UserChartPart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,14 @@ export default function UserChartPart(p: PanelPartContentProps<UserChartPartEnti
);
}

const result = resultOrError?.result;

function handleReload(e?: React.MouseEvent<any>) {
e?.preventDefault();
reloadQuery();
}

const result = resultOrError?.result;
const userChart = toLite(p.content.userChart, true);

return (
<div className="d-flex flex-column flex-grow-1">
<PinnedFilterBuilder filterOptions={chartRequest.filterOptions} onFiltersChanged={() => reloadQuery()} pinnedFilterVisible={fop => fop.dashboardBehaviour == null} extraSmall={true} />
Expand All @@ -175,6 +176,7 @@ export default function UserChartPart(p: PanelPartContentProps<UserChartPartEnti
onReload={handleReload}
/>) :
<ChartRenderer
userChart={userChart}
chartRequest={chartRequest}
lastChartRequest={chartRequest}
data={result?.chartTable}
Expand All @@ -189,7 +191,7 @@ export default function UserChartPart(p: PanelPartContentProps<UserChartPartEnti
onDrillDown={(row, e) => {
e.stopPropagation();
if (e.altKey || p.partEmbedded.interactionGroup == null)
handleDrillDown(row, e, chartRequest, handleReload);
handleDrillDown(row, e, chartRequest, userChart, handleReload);
else {
const dashboardFilter = p.dashboardController.filters.get(p.partEmbedded);
const filterRow = toDashboardFilterRow(row, chartRequest);
Expand Down
30 changes: 18 additions & 12 deletions Signum.React.Extensions/UserQueries/UserQueryClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import SearchControlLoaded, { OnDrilldownOptions } from '@framework/SearchContro
import SelectorModal from '@framework/SelectorModal';
import { DynamicTypeConditionSymbolEntity } from '../Dynamic/Signum.Entities.Dynamic';
import { Dic } from '@framework/Globals';
import { ChartRequestModel } from '../Chart/Signum.Entities.Chart';
import { ChartRequestModel, UserChartEntity } from '../Chart/Signum.Entities.Chart';
import { ChartRow, hasAggregates } from '../Chart/ChartClient';

export function start(options: { routes: RouteObject[] }) {
Expand Down Expand Up @@ -164,22 +164,28 @@ export async function onDrilldownSearchControl(scl: SearchControlLoaded, row: Re
return drilldownToUserQuery(val.fo, val.uq, options);
}

export function onDrilldownUserChart(cr: ChartRequestModel, row: ChartRow, options?: OnDrilldownOptions): Promise<boolean | undefined> {
if (cr.customDrilldowns.length == 0)
return Promise.resolve(false);
export async function onDrilldownUserChart(cr: ChartRequestModel, row: ChartRow, uc?: Lite<UserChartEntity>, options?: OnDrilldownOptions): Promise<boolean | undefined> {
if (uc == null)
return false;

await Navigator.API.fetchAndRemember(uc);

if (uc.entity!.customDrilldowns.length == 0 || hasAggregates(uc.entity!) != hasAggregates(cr))
return false;

debugger;
const fo = extractFindOptions(cr, row);
const entity = row.entity ?? (hasAggregates(cr) ? undefined : fo.filterOptions?.singleOrNull(f => f?.token == "Entity")?.value);

const filters = fo.filterOptions?.notNull();
const promise = entity ? onDrilldownEntity(cr.customDrilldowns, entity) : onDrilldownGroup(cr.customDrilldowns, filters);
return promise
.then(val => {
if (!val)
return undefined;

return drilldownToUserQuery(val.fo, val.uq, options);
});
const val = entity ?
await onDrilldownEntity(uc.entity!.customDrilldowns, entity) :
await onDrilldownGroup(uc.entity!.customDrilldowns, filters);

if (!val)
return undefined;

return drilldownToUserQuery(val.fo, val.uq, options);
}

export function onDrilldownEntity(items: MList<Lite<Entity>>, entity: Lite<Entity>) {
Expand Down
Loading

3 comments on commit 75c713f

@olmobrutall
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Presenting Custom Drilldowns

The awesome work from @mehdy-karimpour brings us a new cool feature to UserChart and UserQuery: Custom Drilldowns.

The idea is to be able to control the SearchControlModal that gets opened when you double click on a SearchControl or ChartRenderer.

Programatically

This can be done programatically by using a new event in SearchControl

export interface SearchControlProps { 
   ...
   onDrilldown?: (scl: SearchControlLoaded, row: ResultRow, options?: OnDrilldownOptions) => Promise<boolean | undefined>;
   ...
}

This method will be invoked when doing double click in a row, or clicking in the stacked layers icon representing a group of rows.

In UserQueries and UserCharts

More often however we want to use custom DrillDowns to chain a parent UserQuery (or UserChart) with the child UserQuery that could be used to show the results.

The child user query needs to have Append Filters set.

image

Note: This option is not new and allows a similar functionality but using the contextual menu, it also requires showContextMenu: fop => true at the QuerySettings.

Then, in the parent query we can add one or more Custom drilldowns.

image

In the (rare) case that more than one custom drill down is selected, the user will be asked what to use

image

The same works for UserCharts

image

Custom Drilldowns also works when the parent query is not grouping, but in this case the child query instead of Append Filters needs to set Entity Type.

Of course the new CustomDrildowns will be expoted / imported to XML together with the parent UserChart/ UserQuery

Remaining work

Currently both UserQuery and UserChart are supported as parent query, but only UserQuery is currently supported as child query.

It could make sense to drill down from a UserChat (or UserQuery) into a a ChartModal, but such control doesn't exist yet.

Any brave dev wants to help?

Thanks Mehdy!

@mehdy-karimpour
Copy link
Contributor

Choose a reason for hiding this comment

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

It`s my pleasure. Thanks for your help 👍

@doganc
Copy link

@doganc doganc commented on 75c713f Feb 8, 2023

Choose a reason for hiding this comment

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

Perfect,

Please sign in to comment.