mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 01:04:25 +08:00
add calendar heat map chart in insights explorer
This commit is contained in:
@@ -0,0 +1,268 @@
|
|||||||
|
<template>
|
||||||
|
<v-chart autoresize :class="finalClass" :style="finalStyle" :option="chartOptions" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useTheme } from 'vuetify';
|
||||||
|
import type { CallbackDataParams } from 'echarts/types/dist/shared';
|
||||||
|
|
||||||
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
|
|
||||||
|
import { type WeekDayValue, KnownDateTimeFormat } from '@/core/datetime.ts';
|
||||||
|
import { ThemeType } from '@/core/theme.ts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
isNumber,
|
||||||
|
getObjectOwnFieldCount,
|
||||||
|
mapObjectToArray
|
||||||
|
} from '@/lib/common.ts';
|
||||||
|
import { parseDateTimeFromKnownDateTimeFormat } from '@/lib/datetime.ts';
|
||||||
|
|
||||||
|
interface HeatMapData {
|
||||||
|
data: Record<number, YearlyHeatmapData>;
|
||||||
|
minValue: number;
|
||||||
|
maxValue: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface YearlyHeatmapData {
|
||||||
|
gregorianYear: number;
|
||||||
|
displayYear: string;
|
||||||
|
data: [string, number][];
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: string;
|
||||||
|
skeleton?: boolean;
|
||||||
|
showValue?: boolean;
|
||||||
|
items: Record<string, unknown>[];
|
||||||
|
idField: string;
|
||||||
|
valueField: string;
|
||||||
|
hiddenField?: string;
|
||||||
|
translateName?: boolean;
|
||||||
|
valueTypeName: string;
|
||||||
|
amountValue?: boolean;
|
||||||
|
percentValue?: boolean;
|
||||||
|
defaultCurrency?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const {
|
||||||
|
tt,
|
||||||
|
getAllShortMonthNames,
|
||||||
|
getAllMinWeekdayNames,
|
||||||
|
formatDateTimeToLongDate,
|
||||||
|
getCalendarDisplayLongYearFromDateTime,
|
||||||
|
formatAmountToLocalizedNumeralsWithCurrency,
|
||||||
|
formatNumberToLocalizedNumerals,
|
||||||
|
formatPercentToLocalizedNumerals
|
||||||
|
} = useI18n();
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const visualMapHeight: number = 100;
|
||||||
|
const calendarHeight: number = 180;
|
||||||
|
const calendarBottomMargin: number = 10;
|
||||||
|
|
||||||
|
const firstDayOfWeek = computed<WeekDayValue>(() => userStore.currentUserFirstDayOfWeek);
|
||||||
|
const dayNames = computed<string[]>(() => getAllMinWeekdayNames());
|
||||||
|
const monthNames = computed<string[]>(() => getAllShortMonthNames());
|
||||||
|
|
||||||
|
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
|
||||||
|
const finalClass = computed<string>(() => {
|
||||||
|
let finalClass = '';
|
||||||
|
|
||||||
|
if (props.skeleton) {
|
||||||
|
finalClass += 'transition-in';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.class) {
|
||||||
|
finalClass += ` ${props.class}`;
|
||||||
|
} else {
|
||||||
|
finalClass += ' calendar-heatmap-chart-container';
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalClass;
|
||||||
|
});
|
||||||
|
const finalStyle = computed<Record<string, string>>(() => {
|
||||||
|
const style: Record<string, string> = {};
|
||||||
|
|
||||||
|
if (heatMapData.value.data) {
|
||||||
|
const calendarCount = getObjectOwnFieldCount(heatMapData.value.data);
|
||||||
|
style['height'] = `${visualMapHeight + calendarCount * calendarHeight + (calendarCount - 1) * calendarBottomMargin}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return style;
|
||||||
|
});
|
||||||
|
|
||||||
|
const heatMapData = computed<HeatMapData>(() => {
|
||||||
|
const allData: Record<number, YearlyHeatmapData> = {};
|
||||||
|
let minValue: number = Number.POSITIVE_INFINITY;
|
||||||
|
let maxValue: number = 0;
|
||||||
|
|
||||||
|
for (const item of props.items) {
|
||||||
|
const id = getItemName(item[props.idField] as string);
|
||||||
|
const dateTime = parseDateTimeFromKnownDateTimeFormat(id, KnownDateTimeFormat.DefaultDate);
|
||||||
|
const value = item[props.valueField];
|
||||||
|
|
||||||
|
if (dateTime && isNumber(value) && (!props.hiddenField || !item[props.hiddenField])) {
|
||||||
|
if (value > maxValue) {
|
||||||
|
maxValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < minValue) {
|
||||||
|
minValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const year: number = dateTime.getGregorianCalendarYear();
|
||||||
|
let data: YearlyHeatmapData | undefined = allData[year];
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
data = {
|
||||||
|
gregorianYear: year,
|
||||||
|
displayYear: getCalendarDisplayLongYearFromDateTime(dateTime),
|
||||||
|
data: []
|
||||||
|
};
|
||||||
|
allData[year] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.data.push([dateTime.getGregorianCalendarYearDashMonthDashDay(), value]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ret: HeatMapData = {
|
||||||
|
data: allData,
|
||||||
|
minValue: minValue === Number.POSITIVE_INFINITY ? 0 : minValue,
|
||||||
|
maxValue: maxValue
|
||||||
|
};
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
const chartOptions = computed<object>(() => {
|
||||||
|
return {
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: isDarkMode.value ? '#333' : '#fff',
|
||||||
|
borderColor: isDarkMode.value ? '#333' : '#fff',
|
||||||
|
textStyle: {
|
||||||
|
color: isDarkMode.value ? '#eee' : '#333'
|
||||||
|
},
|
||||||
|
formatter: (params: CallbackDataParams) => {
|
||||||
|
if (!props.showValue) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataItem = params.data as [string, number];
|
||||||
|
const dateTime = dataItem && dataItem[0] ? parseDateTimeFromKnownDateTimeFormat(dataItem[0], KnownDateTimeFormat.DefaultDate) : '';
|
||||||
|
const name = props.valueTypeName;
|
||||||
|
const value = dataItem && isNumber(dataItem[1]) ? getDisplayValue(dataItem[1]) : '';
|
||||||
|
|
||||||
|
return (dateTime ? `<div class="d-inline-flex">${formatDateTimeToLongDate(dateTime)}</div><br/>` : '')
|
||||||
|
+ `<div><span class="chart-pointer" style="background-color: ${params.color}"></span>`
|
||||||
|
+ `<span>${name}</span>`
|
||||||
|
+ `<span class="ms-5">${value}</span>`
|
||||||
|
+ '</div>';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visualMap: [
|
||||||
|
{
|
||||||
|
type: 'continuous',
|
||||||
|
orient: 'horizontal',
|
||||||
|
top: 0,
|
||||||
|
left: 'center',
|
||||||
|
itemHeight: 320,
|
||||||
|
min: heatMapData.value.minValue,
|
||||||
|
max: heatMapData.value.maxValue,
|
||||||
|
calculable: true,
|
||||||
|
inRange: {
|
||||||
|
color: isDarkMode.value ? [ '#1a1a1a', '#c67e48' ] : [ '#faf8f4', '#c67e48' ]
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: isDarkMode.value ? '#888' : '#666'
|
||||||
|
},
|
||||||
|
formatter: (value: string) => {
|
||||||
|
if (!props.showValue) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDisplayValue(parseInt(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
calendar: mapObjectToArray(heatMapData.value.data, (item, _, index) => {
|
||||||
|
return {
|
||||||
|
range: item.gregorianYear,
|
||||||
|
orient: 'horizontal',
|
||||||
|
left: 70,
|
||||||
|
top: visualMapHeight + index * (calendarHeight + calendarBottomMargin),
|
||||||
|
right: 20,
|
||||||
|
cellSize: ['auto', 20],
|
||||||
|
itemStyle: {
|
||||||
|
color: isDarkMode.value ? '#060504' : '#ffffff',
|
||||||
|
borderColor: isDarkMode.value ? '#4f4f4f' : '#e1e6f2'
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
dayLabel: {
|
||||||
|
firstDay: firstDayOfWeek.value,
|
||||||
|
nameMap: dayNames.value,
|
||||||
|
color: isDarkMode.value ? '#888' : '#666'
|
||||||
|
},
|
||||||
|
monthLabel: {
|
||||||
|
nameMap: monthNames.value,
|
||||||
|
color: isDarkMode.value ? '#888' : '#666'
|
||||||
|
},
|
||||||
|
yearLabel: {
|
||||||
|
formatter: item.displayYear,
|
||||||
|
color: isDarkMode.value ? '#888' : '#666'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
series: mapObjectToArray(heatMapData.value.data, (item, _, index) => {
|
||||||
|
return {
|
||||||
|
type: 'heatmap',
|
||||||
|
animation: !props.skeleton,
|
||||||
|
coordinateSystem: 'calendar',
|
||||||
|
calendarIndex: index,
|
||||||
|
data: item.data,
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
shadowBlur: 6,
|
||||||
|
shadowColor: isDarkMode.value ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.5)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function getItemName(name: string): string {
|
||||||
|
return props.translateName ? tt(name) : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDisplayValue(value: number): string {
|
||||||
|
if (props.percentValue) {
|
||||||
|
return formatPercentToLocalizedNumerals(value, 2, '<0.01');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.amountValue) {
|
||||||
|
return formatAmountToLocalizedNumeralsWithCurrency(value, props.defaultCurrency);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatNumberToLocalizedNumerals(value, 2);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.calendar-heatmap-chart-container {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+59
-52
@@ -1,5 +1,6 @@
|
|||||||
import { type NameValue } from '@/core/base.ts';
|
import { type NameValue } from '@/core/base.ts';
|
||||||
import { DateRange } from '@/core/datetime.ts';
|
import { DateRange } from '@/core/datetime.ts';
|
||||||
|
import { ChartSortingType } from '@/core/statistics.ts';
|
||||||
|
|
||||||
export enum TransactionExplorerConditionRelation {
|
export enum TransactionExplorerConditionRelation {
|
||||||
First = 'first',
|
First = 'first',
|
||||||
@@ -166,58 +167,6 @@ export class TransactionExplorerConditionOperator implements NameValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TransactionExplorerChartTypeValue {
|
|
||||||
Pie = 'pie',
|
|
||||||
ColumnStacked = 'columnStacked',
|
|
||||||
Column100PercentStacked = 'column100%Stacked',
|
|
||||||
ColumnGrouped = 'columnGrouped',
|
|
||||||
LineGrouped = 'lineGrouped',
|
|
||||||
AreaStacked = 'areaStacked',
|
|
||||||
Area100PercentStacked = 'area100%Stacked',
|
|
||||||
BubbleGrouped = 'bubbleGrouped',
|
|
||||||
Radar = 'radar',
|
|
||||||
Heatmap = 'heatmap'
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TransactionExplorerChartType implements NameValue {
|
|
||||||
private static readonly allInstances: TransactionExplorerChartType[] = [];
|
|
||||||
private static readonly allInstancesByValue: Record<string, TransactionExplorerChartType> = {};
|
|
||||||
|
|
||||||
public static readonly Pie = new TransactionExplorerChartType('Pie Chart', TransactionExplorerChartTypeValue.Pie, false);
|
|
||||||
public static readonly Radar = new TransactionExplorerChartType('Radar Chart', TransactionExplorerChartTypeValue.Radar, false);
|
|
||||||
public static readonly ColumnStacked = new TransactionExplorerChartType('Column Chart (Stacked)', TransactionExplorerChartTypeValue.ColumnStacked, true);
|
|
||||||
public static readonly Column100PercentStacked = new TransactionExplorerChartType('Column Chart (100% Stacked)', TransactionExplorerChartTypeValue.Column100PercentStacked, true);
|
|
||||||
public static readonly ColumnGrouped = new TransactionExplorerChartType('Column Chart (Grouped)', TransactionExplorerChartTypeValue.ColumnGrouped, true);
|
|
||||||
public static readonly LineGrouped = new TransactionExplorerChartType('Line Chart (Grouped)', TransactionExplorerChartTypeValue.LineGrouped, true);
|
|
||||||
public static readonly AreaStacked = new TransactionExplorerChartType('Area Chart (Stacked)', TransactionExplorerChartTypeValue.AreaStacked, true);
|
|
||||||
public static readonly Area100PercentStacked = new TransactionExplorerChartType('Area Chart (100% Stacked)', TransactionExplorerChartTypeValue.Area100PercentStacked, true);
|
|
||||||
public static readonly BubbleGrouped = new TransactionExplorerChartType('Bubble Chart (Grouped)', TransactionExplorerChartTypeValue.BubbleGrouped, true);
|
|
||||||
public static readonly Heatmap = new TransactionExplorerChartType('Heatmap Chart', TransactionExplorerChartTypeValue.Heatmap, true);
|
|
||||||
|
|
||||||
public static readonly Default = TransactionExplorerChartType.Pie;
|
|
||||||
|
|
||||||
public readonly name: string;
|
|
||||||
public readonly value: TransactionExplorerChartTypeValue;
|
|
||||||
public readonly seriesDimensionRequired: boolean;
|
|
||||||
|
|
||||||
private constructor(name: string, value: TransactionExplorerChartTypeValue, seriesDimensionRequired: boolean) {
|
|
||||||
this.name = name;
|
|
||||||
this.value = value;
|
|
||||||
this.seriesDimensionRequired = seriesDimensionRequired;
|
|
||||||
|
|
||||||
TransactionExplorerChartType.allInstances.push(this);
|
|
||||||
TransactionExplorerChartType.allInstancesByValue[value] = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static values(): TransactionExplorerChartType[] {
|
|
||||||
return TransactionExplorerChartType.allInstances;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static valueOf(value: string): TransactionExplorerChartType | undefined {
|
|
||||||
return TransactionExplorerChartType.allInstancesByValue[value];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum TransactionExplorerDataDimensionType {
|
export enum TransactionExplorerDataDimensionType {
|
||||||
None = 'none',
|
None = 'none',
|
||||||
Query = 'query',
|
Query = 'query',
|
||||||
@@ -421,4 +370,62 @@ export class TransactionExplorerValueMetric implements NameValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum TransactionExplorerChartTypeValue {
|
||||||
|
Pie = 'pie',
|
||||||
|
ColumnStacked = 'columnStacked',
|
||||||
|
Column100PercentStacked = 'column100%Stacked',
|
||||||
|
ColumnGrouped = 'columnGrouped',
|
||||||
|
LineGrouped = 'lineGrouped',
|
||||||
|
AreaStacked = 'areaStacked',
|
||||||
|
Area100PercentStacked = 'area100%Stacked',
|
||||||
|
BubbleGrouped = 'bubbleGrouped',
|
||||||
|
Radar = 'radar',
|
||||||
|
Heatmap = 'heatmap',
|
||||||
|
CalendarHeatmap = 'calendarHeatmap'
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TransactionExplorerChartType implements NameValue {
|
||||||
|
private static readonly allInstances: TransactionExplorerChartType[] = [];
|
||||||
|
private static readonly allInstancesByValue: Record<string, TransactionExplorerChartType> = {};
|
||||||
|
|
||||||
|
public static readonly Pie = new TransactionExplorerChartType('Pie Chart', TransactionExplorerChartTypeValue.Pie, undefined, false, undefined);
|
||||||
|
public static readonly Radar = new TransactionExplorerChartType('Radar Chart', TransactionExplorerChartTypeValue.Radar, undefined, false, undefined);
|
||||||
|
public static readonly ColumnStacked = new TransactionExplorerChartType('Column Chart (Stacked)', TransactionExplorerChartTypeValue.ColumnStacked, undefined, true, undefined);
|
||||||
|
public static readonly Column100PercentStacked = new TransactionExplorerChartType('Column Chart (100% Stacked)', TransactionExplorerChartTypeValue.Column100PercentStacked, undefined, true, undefined);
|
||||||
|
public static readonly ColumnGrouped = new TransactionExplorerChartType('Column Chart (Grouped)', TransactionExplorerChartTypeValue.ColumnGrouped, undefined, true, undefined);
|
||||||
|
public static readonly LineGrouped = new TransactionExplorerChartType('Line Chart (Grouped)', TransactionExplorerChartTypeValue.LineGrouped, undefined, true, undefined);
|
||||||
|
public static readonly AreaStacked = new TransactionExplorerChartType('Area Chart (Stacked)', TransactionExplorerChartTypeValue.AreaStacked, undefined, true, undefined);
|
||||||
|
public static readonly Area100PercentStacked = new TransactionExplorerChartType('Area Chart (100% Stacked)', TransactionExplorerChartTypeValue.Area100PercentStacked, undefined, true, undefined);
|
||||||
|
public static readonly BubbleGrouped = new TransactionExplorerChartType('Bubble Chart (Grouped)', TransactionExplorerChartTypeValue.BubbleGrouped, undefined, true, undefined);
|
||||||
|
public static readonly Heatmap = new TransactionExplorerChartType('Heatmap Chart', TransactionExplorerChartTypeValue.Heatmap, undefined, true, undefined);
|
||||||
|
public static readonly CalendarHeatmap = new TransactionExplorerChartType('Calendar Heatmap Chart', TransactionExplorerChartTypeValue.CalendarHeatmap, TransactionExplorerDataDimensionType.DateTimeByYearMonthDay, false, ChartSortingType.DisplayOrder.type);
|
||||||
|
|
||||||
|
public static readonly Default = TransactionExplorerChartType.Pie;
|
||||||
|
|
||||||
|
public readonly name: string;
|
||||||
|
public readonly value: TransactionExplorerChartTypeValue;
|
||||||
|
public readonly fixedCategoryDimension: TransactionExplorerDataDimensionType | undefined;
|
||||||
|
public readonly seriesDimensionRequired: boolean;
|
||||||
|
public readonly fixedSortingType: number | undefined;
|
||||||
|
|
||||||
|
private constructor(name: string, value: TransactionExplorerChartTypeValue, fixedCategoryDimension: TransactionExplorerDataDimensionType | undefined, seriesDimensionRequired: boolean, fixedSortingType: number | undefined) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
this.fixedCategoryDimension = fixedCategoryDimension;
|
||||||
|
this.seriesDimensionRequired = seriesDimensionRequired;
|
||||||
|
this.fixedSortingType = fixedSortingType;
|
||||||
|
|
||||||
|
TransactionExplorerChartType.allInstances.push(this);
|
||||||
|
TransactionExplorerChartType.allInstancesByValue[value] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static values(): TransactionExplorerChartType[] {
|
||||||
|
return TransactionExplorerChartType.allInstances;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static valueOf(value: string): TransactionExplorerChartType | undefined {
|
||||||
|
return TransactionExplorerChartType.allInstancesByValue[value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const DEFAULT_TRANSACTION_EXPLORER_DATE_RANGE: DateRange = DateRange.ThisMonth;
|
export const DEFAULT_TRANSACTION_EXPLORER_DATE_RANGE: DateRange = DateRange.ThisMonth;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ import {
|
|||||||
} from 'echarts/charts';
|
} from 'echarts/charts';
|
||||||
import {
|
import {
|
||||||
GridComponent,
|
GridComponent,
|
||||||
|
CalendarComponent,
|
||||||
TooltipComponent,
|
TooltipComponent,
|
||||||
LegendComponent,
|
LegendComponent,
|
||||||
VisualMapComponent
|
VisualMapComponent
|
||||||
@@ -115,6 +116,7 @@ import RadarChartComponent from '@/components/desktop/RadarChart.vue';
|
|||||||
import AxisChart from '@/components/desktop/AxisChart.vue';
|
import AxisChart from '@/components/desktop/AxisChart.vue';
|
||||||
import TrendsChart from '@/components/desktop/TrendsChart.vue';
|
import TrendsChart from '@/components/desktop/TrendsChart.vue';
|
||||||
import HeatMapChart from '@/components/desktop/HeatMapChart.vue';
|
import HeatMapChart from '@/components/desktop/HeatMapChart.vue';
|
||||||
|
import CalendarHeatMapChart from '@/components/desktop/CalendarHeatMapChart.vue';
|
||||||
import RenameDialog from '@/components/desktop/RenameDialog.vue';
|
import RenameDialog from '@/components/desktop/RenameDialog.vue';
|
||||||
import DateRangeSelectionDialog from '@/components/desktop/DateRangeSelectionDialog.vue';
|
import DateRangeSelectionDialog from '@/components/desktop/DateRangeSelectionDialog.vue';
|
||||||
import MonthSelectionDialog from '@/components/desktop/MonthSelectionDialog.vue';
|
import MonthSelectionDialog from '@/components/desktop/MonthSelectionDialog.vue';
|
||||||
@@ -521,6 +523,7 @@ echarts.use([
|
|||||||
HeatmapChart,
|
HeatmapChart,
|
||||||
SankeyChart,
|
SankeyChart,
|
||||||
GridComponent,
|
GridComponent,
|
||||||
|
CalendarComponent,
|
||||||
TooltipComponent,
|
TooltipComponent,
|
||||||
LegendComponent,
|
LegendComponent,
|
||||||
VisualMapComponent
|
VisualMapComponent
|
||||||
@@ -566,6 +569,7 @@ app.component('RadarChart', RadarChartComponent);
|
|||||||
app.component('AxisChart', AxisChart);
|
app.component('AxisChart', AxisChart);
|
||||||
app.component('TrendsChart', TrendsChart);
|
app.component('TrendsChart', TrendsChart);
|
||||||
app.component('HeatMapChart', HeatMapChart);
|
app.component('HeatMapChart', HeatMapChart);
|
||||||
|
app.component('CalendarHeatMapChart', CalendarHeatMapChart);
|
||||||
app.component('RenameDialog', RenameDialog);
|
app.component('RenameDialog', RenameDialog);
|
||||||
app.component('DateRangeSelectionDialog', DateRangeSelectionDialog);
|
app.component('DateRangeSelectionDialog', DateRangeSelectionDialog);
|
||||||
app.component('MonthSelectionDialog', MonthSelectionDialog);
|
app.component('MonthSelectionDialog', MonthSelectionDialog);
|
||||||
|
|||||||
+17
-1
@@ -2,9 +2,10 @@ import {
|
|||||||
type GenericNameValue,
|
type GenericNameValue,
|
||||||
type TypeAndName,
|
type TypeAndName,
|
||||||
type TypeAndDisplayName,
|
type TypeAndDisplayName,
|
||||||
|
entries,
|
||||||
keys,
|
keys,
|
||||||
keysIfValueEquals,
|
keysIfValueEquals,
|
||||||
values
|
values,
|
||||||
} from '@/core/base.ts';
|
} from '@/core/base.ts';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
@@ -387,6 +388,21 @@ export function objectFieldToArrayItem(object: object): string[] {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mapObjectToArray<V, R>(object: Record<string | number | symbol, V>, mapFunc: (value: V, key: string | number | symbol, index: number) => R): R[] {
|
||||||
|
const ret: R[] = [];
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
|
for (const [key, value] of entries(object)) {
|
||||||
|
const mappedValue = mapFunc(value, key, index++);
|
||||||
|
|
||||||
|
if (isDefined(mappedValue)) {
|
||||||
|
ret.push(mappedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
export function objectFieldWithValueToArrayItem<T>(object: Record<string, T>, value: T): string[] {
|
export function objectFieldWithValueToArrayItem<T>(object: Record<string, T>, value: T): string[] {
|
||||||
const ret: string[] = [];
|
const ret: string[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Flächendiagramm (100% Gestapelt)",
|
"Area Chart (100% Stacked)": "Flächendiagramm (100% Gestapelt)",
|
||||||
"Bubble Chart (Grouped)": "Blasendiagramm (Gruppiert)",
|
"Bubble Chart (Grouped)": "Blasendiagramm (Gruppiert)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Sortieren nach",
|
"Sort by": "Sortieren nach",
|
||||||
"Map": "Karte",
|
"Map": "Karte",
|
||||||
"Provider": "Anbieter",
|
"Provider": "Anbieter",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
||||||
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Sort by",
|
"Sort by": "Sort by",
|
||||||
"Map": "Map",
|
"Map": "Map",
|
||||||
"Provider": "Provider",
|
"Provider": "Provider",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Gráfico de área (100 % apilado)",
|
"Area Chart (100% Stacked)": "Gráfico de área (100 % apilado)",
|
||||||
"Bubble Chart (Grouped)": "Gráfico de burbujas (agrupado)",
|
"Bubble Chart (Grouped)": "Gráfico de burbujas (agrupado)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Ordenar por",
|
"Sort by": "Ordenar por",
|
||||||
"Map": "Mapa",
|
"Map": "Mapa",
|
||||||
"Provider": "Proveedor",
|
"Provider": "Proveedor",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
||||||
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Trier par",
|
"Sort by": "Trier par",
|
||||||
"Map": "Carte",
|
"Map": "Carte",
|
||||||
"Provider": "Fournisseur",
|
"Provider": "Fournisseur",
|
||||||
|
|||||||
@@ -2617,6 +2617,7 @@ export function useI18n() {
|
|||||||
isLongTimeMinuteTwoDigits,
|
isLongTimeMinuteTwoDigits,
|
||||||
isLongTimeSecondTwoDigits,
|
isLongTimeSecondTwoDigits,
|
||||||
// format date time (by calendar display type) functions
|
// format date time (by calendar display type) functions
|
||||||
|
getCalendarDisplayLongYearFromDateTime: (dateTime: DateTime, numeralSystem?: NumeralSystem) => formatDateTime(dateTime, getLocalizedLongYearFormat(), getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem })),
|
||||||
getCalendarDisplayShortYearFromDateTime: (dateTime: DateTime, numeralSystem?: NumeralSystem) => formatDateTime(dateTime, getLocalizedShortYearFormat(), getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem })),
|
getCalendarDisplayShortYearFromDateTime: (dateTime: DateTime, numeralSystem?: NumeralSystem) => formatDateTime(dateTime, getLocalizedShortYearFormat(), getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem })),
|
||||||
getCalendarDisplayShortMonthFromDateTime: (dateTime: DateTime, numeralSystem?: NumeralSystem) => formatDateTime(dateTime, 'MMM', getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem })),
|
getCalendarDisplayShortMonthFromDateTime: (dateTime: DateTime, numeralSystem?: NumeralSystem) => formatDateTime(dateTime, 'MMM', getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem })),
|
||||||
getCalendarDisplayDayOfMonthFromDateTime: (dateTime: DateTime, numeralSystem?: NumeralSystem) => formatDateTime(dateTime, getLocalizedShortDayFormat(), getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem })),
|
getCalendarDisplayDayOfMonthFromDateTime: (dateTime: DateTime, numeralSystem?: NumeralSystem) => formatDateTime(dateTime, getLocalizedShortDayFormat(), getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem })),
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
||||||
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Ordina per",
|
"Sort by": "Ordina per",
|
||||||
"Map": "Mappa",
|
"Map": "Mappa",
|
||||||
"Provider": "Fornitore",
|
"Provider": "Fornitore",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
||||||
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "ソート順",
|
"Sort by": "ソート順",
|
||||||
"Map": "地図",
|
"Map": "地図",
|
||||||
"Provider": "プロバイダー",
|
"Provider": "プロバイダー",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
||||||
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "ಇದರ ಪ್ರಕಾರ ವಿಂಗಡಿಸಿ",
|
"Sort by": "ಇದರ ಪ್ರಕಾರ ವಿಂಗಡಿಸಿ",
|
||||||
"Map": "ನಕ್ಷೆ",
|
"Map": "ನಕ್ಷೆ",
|
||||||
"Provider": "ಪ್ರದಾತ",
|
"Provider": "ಪ್ರದಾತ",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "100% 누적 영역 차트",
|
"Area Chart (100% Stacked)": "100% 누적 영역 차트",
|
||||||
"Bubble Chart (Grouped)": "그룹화된 버블 차트",
|
"Bubble Chart (Grouped)": "그룹화된 버블 차트",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "정렬 기준",
|
"Sort by": "정렬 기준",
|
||||||
"Map": "지도",
|
"Map": "지도",
|
||||||
"Provider": "제공자",
|
"Provider": "제공자",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
||||||
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Sorteren op",
|
"Sort by": "Sorteren op",
|
||||||
"Map": "Kaart",
|
"Map": "Kaart",
|
||||||
"Provider": "Provider",
|
"Provider": "Provider",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Gráfico de Área (100% Empilhado)",
|
"Area Chart (100% Stacked)": "Gráfico de Área (100% Empilhado)",
|
||||||
"Bubble Chart (Grouped)": "Gráfico de Bolhas (Agrupado)",
|
"Bubble Chart (Grouped)": "Gráfico de Bolhas (Agrupado)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Ordenar por",
|
"Sort by": "Ordenar por",
|
||||||
"Map": "Mapa",
|
"Map": "Mapa",
|
||||||
"Provider": "Provedor",
|
"Provider": "Provedor",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Диаграмма с областями (с 100% накоплением)",
|
"Area Chart (100% Stacked)": "Диаграмма с областями (с 100% накоплением)",
|
||||||
"Bubble Chart (Grouped)": "Пузырьковая диаграмма (сгрупированная)",
|
"Bubble Chart (Grouped)": "Пузырьковая диаграмма (сгрупированная)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Сортировать по",
|
"Sort by": "Сортировать по",
|
||||||
"Map": "Карта",
|
"Map": "Карта",
|
||||||
"Provider": "Провайдер",
|
"Provider": "Провайдер",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "100-odstotno naložen ploskovni grafikon",
|
"Area Chart (100% Stacked)": "100-odstotno naložen ploskovni grafikon",
|
||||||
"Bubble Chart (Grouped)": "Grupiran mehurčni grafikon",
|
"Bubble Chart (Grouped)": "Grupiran mehurčni grafikon",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Razvrsti po",
|
"Sort by": "Razvrsti po",
|
||||||
"Map": "Zemljevid",
|
"Map": "Zemljevid",
|
||||||
"Provider": "Ponudnik",
|
"Provider": "Ponudnik",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "பரப்பு வரைபடம் (100% அடுக்கப்பட்ட)",
|
"Area Chart (100% Stacked)": "பரப்பு வரைபடம் (100% அடுக்கப்பட்ட)",
|
||||||
"Bubble Chart (Grouped)": "குமிழி வரைபடம் (குழுவாக்கப்பட்ட)",
|
"Bubble Chart (Grouped)": "குமிழி வரைபடம் (குழுவாக்கப்பட்ட)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "இதன் வகை வரிசைப்படுத்து",
|
"Sort by": "இதன் வகை வரிசைப்படுத்து",
|
||||||
"Map": "வரைபடம்",
|
"Map": "வரைபடம்",
|
||||||
"Provider": "வழங்குநர்",
|
"Provider": "வழங்குநர்",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
||||||
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "จัดเรียงตาม",
|
"Sort by": "จัดเรียงตาม",
|
||||||
"Map": "แผนที่",
|
"Map": "แผนที่",
|
||||||
"Provider": "ผู้ให้บริการ",
|
"Provider": "ผู้ให้บริการ",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
||||||
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Sıralama ölçütü",
|
"Sort by": "Sıralama ölçütü",
|
||||||
"Map": "Harita",
|
"Map": "Harita",
|
||||||
"Provider": "Sağlayıcı",
|
"Provider": "Sağlayıcı",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
||||||
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Сортувати за",
|
"Sort by": "Сортувати за",
|
||||||
"Map": "Карта",
|
"Map": "Карта",
|
||||||
"Provider": "Провайдер",
|
"Provider": "Провайдер",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
"Area Chart (100% Stacked)": "Area Chart (100% Stacked)",
|
||||||
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
"Bubble Chart (Grouped)": "Bubble Chart (Grouped)",
|
||||||
"Heatmap Chart": "Heatmap Chart",
|
"Heatmap Chart": "Heatmap Chart",
|
||||||
|
"Calendar Heatmap Chart": "Calendar Heatmap Chart",
|
||||||
"Sort by": "Sắp xếp theo",
|
"Sort by": "Sắp xếp theo",
|
||||||
"Map": "Bản đồ",
|
"Map": "Bản đồ",
|
||||||
"Provider": "Nhà cung cấp",
|
"Provider": "Nhà cung cấp",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "面积图(100%堆叠)",
|
"Area Chart (100% Stacked)": "面积图(100%堆叠)",
|
||||||
"Bubble Chart (Grouped)": "气泡图(分组)",
|
"Bubble Chart (Grouped)": "气泡图(分组)",
|
||||||
"Heatmap Chart": "热力图",
|
"Heatmap Chart": "热力图",
|
||||||
|
"Calendar Heatmap Chart": "日历热力图",
|
||||||
"Sort by": "排序方式",
|
"Sort by": "排序方式",
|
||||||
"Map": "地图",
|
"Map": "地图",
|
||||||
"Provider": "提供者",
|
"Provider": "提供者",
|
||||||
|
|||||||
@@ -1604,6 +1604,7 @@
|
|||||||
"Area Chart (100% Stacked)": "面積圖(100%堆疊)",
|
"Area Chart (100% Stacked)": "面積圖(100%堆疊)",
|
||||||
"Bubble Chart (Grouped)": "氣泡圖(分組)",
|
"Bubble Chart (Grouped)": "氣泡圖(分組)",
|
||||||
"Heatmap Chart": "熱力圖",
|
"Heatmap Chart": "熱力圖",
|
||||||
|
"Calendar Heatmap Chart": "日曆熱力圖",
|
||||||
"Sort by": "排序方式",
|
"Sort by": "排序方式",
|
||||||
"Map": "地圖",
|
"Map": "地圖",
|
||||||
"Provider": "提供者",
|
"Provider": "提供者",
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
:disabled="loading || disabled"
|
:disabled="loading || disabled"
|
||||||
:label="tt('Chart Type')"
|
:label="tt('Chart Type')"
|
||||||
:items="allTransactionExplorerChartTypes"
|
:items="allTransactionExplorerChartTypes"
|
||||||
v-model="currentExplorer.chartType"
|
:model-value="currentExplorer.chartType"
|
||||||
|
@update:model-value="updateChartType"
|
||||||
/>
|
/>
|
||||||
<v-select
|
<v-select
|
||||||
class="flex-0-0"
|
class="flex-0-0"
|
||||||
@@ -20,10 +21,10 @@
|
|||||||
item-title="name"
|
item-title="name"
|
||||||
item-value="value"
|
item-value="value"
|
||||||
density="compact"
|
density="compact"
|
||||||
:disabled="loading || disabled"
|
:disabled="loading || disabled || isDefined(TransactionExplorerChartType.valueOf(currentExplorer.chartType)?.fixedCategoryDimension)"
|
||||||
:label="tt('Axis / Category')"
|
:label="tt('Axis / Category')"
|
||||||
:items="allTransactionExplorerDataDimensions"
|
:items="allTransactionExplorerDataDimensions"
|
||||||
:model-value="currentExplorer.categoryDimension"
|
:model-value="TransactionExplorerChartType.valueOf(currentExplorer.chartType)?.fixedCategoryDimension ?? currentExplorer.categoryDimension"
|
||||||
@update:model-value="updateCategoryDimensionType"
|
@update:model-value="updateCategoryDimensionType"
|
||||||
/>
|
/>
|
||||||
<v-select
|
<v-select
|
||||||
@@ -75,7 +76,7 @@
|
|||||||
item-title="displayName"
|
item-title="displayName"
|
||||||
item-value="type"
|
item-value="type"
|
||||||
density="compact"
|
density="compact"
|
||||||
:disabled="loading || disabled"
|
:disabled="loading || disabled || isDefined(TransactionExplorerChartType.valueOf(currentExplorer.chartType)?.fixedSortingType)"
|
||||||
:label="tt('Sort Order')"
|
:label="tt('Sort Order')"
|
||||||
:items="allTransactionExplorerChartSortingTypes"
|
:items="allTransactionExplorerChartSortingTypes"
|
||||||
v-model="currentExplorer.chartSortingType"
|
v-model="currentExplorer.chartSortingType"
|
||||||
@@ -203,6 +204,27 @@
|
|||||||
v-else-if="!loading"
|
v-else-if="!loading"
|
||||||
/>
|
/>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
<v-card-text :class="{ 'readonly': loading }" v-else-if="currentExplorer.chartType === TransactionExplorerChartType.CalendarHeatmap.value">
|
||||||
|
<calendar-heat-map-chart
|
||||||
|
:skeleton="true"
|
||||||
|
:items="[]"
|
||||||
|
:value-type-name="tt(TransactionExplorerValueMetric.valueOf(currentExplorer.valueMetric)?.name ?? 'Value')"
|
||||||
|
id-field="id"
|
||||||
|
value-field="totalAmount"
|
||||||
|
v-if="loading"
|
||||||
|
/>
|
||||||
|
<calendar-heat-map-chart
|
||||||
|
:show-value="true"
|
||||||
|
:items="categoryDimensionTransactionExplorerData && categoryDimensionTransactionExplorerData.length ? categoryDimensionTransactionExplorerData : []"
|
||||||
|
:value-type-name="tt(TransactionExplorerValueMetric.valueOf(currentExplorer.valueMetric)?.name ?? 'Value')"
|
||||||
|
:amount-value="TransactionExplorerValueMetric.valueOf(currentExplorer.valueMetric)?.isAmount"
|
||||||
|
:percent-value="TransactionExplorerValueMetric.valueOf(currentExplorer.valueMetric)?.isPercent"
|
||||||
|
:default-currency="defaultCurrency"
|
||||||
|
id-field="id"
|
||||||
|
value-field="totalAmount"
|
||||||
|
v-else-if="!loading"
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -228,10 +250,11 @@ import { NumeralSystem } from '@/core/numeral.ts';
|
|||||||
import { Month, WeekDay } from '@/core/datetime.ts';
|
import { Month, WeekDay } from '@/core/datetime.ts';
|
||||||
import { ChartSortingType, ExportMermaidChartType } from '@/core/statistics.ts';
|
import { ChartSortingType, ExportMermaidChartType } from '@/core/statistics.ts';
|
||||||
import {
|
import {
|
||||||
TransactionExplorerChartType,
|
|
||||||
TransactionExplorerDataDimensionType,
|
TransactionExplorerDataDimensionType,
|
||||||
TransactionExplorerDataDimension,
|
TransactionExplorerDataDimension,
|
||||||
TransactionExplorerValueMetric
|
TransactionExplorerValueMetric,
|
||||||
|
TransactionExplorerChartTypeValue,
|
||||||
|
TransactionExplorerChartType
|
||||||
} from '@/core/explorer.ts';
|
} from '@/core/explorer.ts';
|
||||||
|
|
||||||
import { type SortableTransactionStatisticDataItem } from '@/models/transaction.ts';
|
import { type SortableTransactionStatisticDataItem } from '@/models/transaction.ts';
|
||||||
@@ -328,7 +351,9 @@ const allAmountRangeCounts = computed<NameNumeralValue[]>(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const categoryDimensionTransactionExplorerData = computed<CategoryDimensionData[]>(() => {
|
const categoryDimensionTransactionExplorerData = computed<CategoryDimensionData[]>(() => {
|
||||||
if (currentExplorer.value.chartType !== TransactionExplorerChartType.Pie.value && currentExplorer.value.chartType !== TransactionExplorerChartType.Radar.value) {
|
if (currentExplorer.value.chartType !== TransactionExplorerChartType.Pie.value
|
||||||
|
&& currentExplorer.value.chartType !== TransactionExplorerChartType.Radar.value
|
||||||
|
&& currentExplorer.value.chartType !== TransactionExplorerChartType.CalendarHeatmap.value) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,6 +820,26 @@ function getAxisChartTooltipExtraColumnValues(seriesId: string, categoryIndex: n
|
|||||||
return extraColumnValues;
|
return extraColumnValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateChartType(chartTypeValue: TransactionExplorerChartTypeValue): void {
|
||||||
|
if (currentExplorer.value.chartType !== chartTypeValue) {
|
||||||
|
currentExplorer.value.chartType = chartTypeValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chartType = TransactionExplorerChartType.valueOf(chartTypeValue);
|
||||||
|
|
||||||
|
if (isDefined(chartType?.fixedCategoryDimension)) {
|
||||||
|
currentExplorer.value.categoryDimension = chartType.fixedCategoryDimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chartType?.seriesDimensionRequired) {
|
||||||
|
currentExplorer.value.seriesDimension = TransactionExplorerDataDimension.None.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDefined(chartType?.fixedSortingType)) {
|
||||||
|
currentExplorer.value.chartSortingType = chartType.fixedSortingType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateCategoryDimensionType(dimensionType: TransactionExplorerDataDimensionType): void {
|
function updateCategoryDimensionType(dimensionType: TransactionExplorerDataDimensionType): void {
|
||||||
if (currentExplorer.value.categoryDimension !== dimensionType) {
|
if (currentExplorer.value.categoryDimension !== dimensionType) {
|
||||||
currentExplorer.value.categoryDimension = dimensionType;
|
currentExplorer.value.categoryDimension = dimensionType;
|
||||||
|
|||||||
Reference in New Issue
Block a user