add activity period filter to user dialog heatmap

This commit is contained in:
pa
2026-03-16 20:57:24 +09:00
parent 54572f9480
commit cfda4c49d1
2 changed files with 61 additions and 2 deletions

View File

@@ -15,6 +15,21 @@
{{ t('dialog.user.activity.total_events', { count: totalOnlineEvents }) }}
</span>
</div>
<div v-if="totalOnlineEvents > 0" style="display: flex; align-items: center">
<span style="margin-right: 6px" class="text-muted-foreground text-xs">{{ t('dialog.user.activity.period') }}</span>
<Select v-model="selectedPeriod" :disabled="isLoading">
<SelectTrigger size="sm" class="w-[130px]" @click.stop>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">{{ t('dialog.user.activity.period_all') }}</SelectItem>
<SelectItem value="365">{{ t('dialog.user.activity.period_365') }}</SelectItem>
<SelectItem value="180">{{ t('dialog.user.activity.period_180') }}</SelectItem>
<SelectItem value="90">{{ t('dialog.user.activity.period_90') }}</SelectItem>
<SelectItem value="30">{{ t('dialog.user.activity.period_30') }}</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div v-if="peakDayText || peakTimeText" class="mt-2 mb-1 text-sm flex gap-4">
<div v-if="peakDayText">
@@ -29,8 +44,11 @@
<div v-if="!isLoading && totalOnlineEvents === 0" class="flex items-center justify-center flex-1 mt-8">
<DataTableEmpty type="nodata" />
</div>
<div v-if="!isLoading && totalOnlineEvents > 0 && filteredEventCount === 0" class="flex items-center justify-center flex-1 mt-8">
<span class="text-muted-foreground text-sm">{{ t('dialog.user.activity.no_data_in_period') }}</span>
</div>
<div
v-show="totalOnlineEvents > 0"
v-show="filteredEventCount > 0"
ref="chartRef"
style="width: 100%; height: 240px"
@contextmenu.prevent="onChartRightClick">
@@ -42,6 +60,7 @@
import { computed, h, nextTick, onBeforeUnmount, ref, watch } from 'vue';
import { Button } from '@/components/ui/button';
import { DataTableEmpty } from '@/components/ui/data-table';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { RefreshCw, Tractor } from 'lucide-vue-next';
import { Spinner } from '@/components/ui/spinner';
import { storeToRefs } from 'pinia';
@@ -63,6 +82,8 @@
const totalOnlineEvents = ref(0);
const peakDayText = ref('');
const peakTimeText = ref('');
const selectedPeriod = ref('all');
const filteredEventCount = ref(0);
let echartsInstance = null;
let resizeObserver = null;
@@ -110,6 +131,11 @@
watch(() => isDarkMode.value, rebuildChart);
watch(locale, rebuildChart);
watch(selectedPeriod, () => {
if (cachedTimestamps.length > 0 && echartsInstance) {
initChart();
}
});
onBeforeUnmount(() => {
disposeChart();
@@ -126,6 +152,14 @@
}
}
function getFilteredTimestamps() {
if (selectedPeriod.value === 'all') return cachedTimestamps;
const days = parseInt(selectedPeriod.value, 10);
const cutoff = dayjs().subtract(days, 'day');
return cachedTimestamps.filter((ts) => dayjs(ts).isAfter(cutoff));
}
/**
* @param {string[]} timestamps
* @returns {{ data: number[][], maxVal: number, peakText: string }}
@@ -210,7 +244,15 @@
function initChart() {
if (!chartRef.value || !echartsInstance) return;
const { data, maxVal, peakDayResult, peakTimeResult } = aggregateHeatmapData(cachedTimestamps);
const filtered = getFilteredTimestamps();
filteredEventCount.value = filtered.length;
if (filtered.length === 0) {
peakDayText.value = '';
peakTimeText.value = '';
return;
}
const { data, maxVal, peakDayResult, peakTimeResult } = aggregateHeatmapData(filtered);
peakDayText.value = peakDayResult;
peakTimeText.value = peakTimeResult;
@@ -301,6 +343,10 @@
const userId = userDialog.value.id;
if (!userId) return;
if (userId !== lastLoadedUserId) {
selectedPeriod.value = 'all';
}
const requestId = ++activeRequestId;
isLoading.value = true;
try {
@@ -314,6 +360,11 @@
await nextTick();
if (timestamps.length > 0) {
const filtered = getFilteredTimestamps();
filteredEventCount.value = filtered.length;
await nextTick();
if (!echartsInstance && chartRef.value) {
echartsInstance = echarts.init(
chartRef.value,
@@ -335,6 +386,7 @@
} else {
peakDayText.value = '';
peakTimeText.value = '';
filteredEventCount.value = 0;
}
} catch (error) {
console.error('Error loading online frequency data:', error);

View File

@@ -1286,6 +1286,13 @@
"times_online": "times online",
"most_active_day": "Most active day:",
"most_active_time": "Peak hours:",
"period": "Period:",
"period_all": "All Time",
"period_365": "Last Year",
"period_180": "Last 6 Months",
"period_90": "Last 90 Days",
"period_30": "Last 30 Days",
"no_data_in_period": "No activity data in selected period",
"days": {
"mon": "Mon",
"tue": "Tue",