diff --git a/src/composables/useDashboardStats.ts b/src/composables/useDashboardStats.ts
index 8b42d87..59bcc48 100644
--- a/src/composables/useDashboardStats.ts
+++ b/src/composables/useDashboardStats.ts
@@ -7,6 +7,9 @@ import type {
DashboardActivityViewItem,
DashboardData,
DashboardHealthSummary,
+ DashboardInsightSeverity,
+ DashboardOperationalInsight,
+ DashboardOperationalInsightViewItem,
DashboardViewData
} from '@/types'
import { formatFileSize, getErrorMessage } from '@/utils/common'
@@ -49,6 +52,7 @@ const emptyDashboardData = (): DashboardViewData => ({
topSuffixes: [],
recentFiles: [],
recentActivities: [],
+ operationalInsights: [],
storageUsedText: '0 Bytes',
yesterdaySizeText: '0 Bytes',
todaySizeText: '0 Bytes',
@@ -109,6 +113,45 @@ const normalizeActivityOptions = (
count: toNumber(option.count)
}))
+const insightSeverities: DashboardInsightSeverity[] = ['danger', 'warning', 'success', 'neutral']
+
+const normalizeInsightSeverity = (severity: string | undefined): DashboardInsightSeverity =>
+ insightSeverities.includes(severity as DashboardInsightSeverity)
+ ? (severity as DashboardInsightSeverity)
+ : 'neutral'
+
+const normalizeOperationalInsights = (
+ insights: DashboardOperationalInsight[] = []
+): DashboardOperationalInsightViewItem[] =>
+ insights
+ .filter((insight) => insight && insight.key)
+ .map((insight) => {
+ const action = insight.action && typeof insight.action === 'object' ? insight.action : {}
+ const actionTypeValue =
+ insight.actionType ??
+ insight.action_type ??
+ action.actionType ??
+ action.action_type ??
+ action.type ??
+ 'file_queue'
+ const targetHealthValue =
+ insight.targetHealth ??
+ insight.target_health ??
+ action.targetHealth ??
+ action.target_health ??
+ action.health ??
+ null
+
+ return {
+ ...insight,
+ count: toNumber(insight.count),
+ priority: toNumber(insight.priority),
+ severity: normalizeInsightSeverity(insight.severity),
+ actionTypeValue,
+ targetHealthValue
+ }
+ })
+
const healthSummaryKeys: (keyof DashboardHealthSummary)[] = [
'healthAttentionCount',
'healthDangerCount',
@@ -210,6 +253,9 @@ export function useDashboardStats(options: UseDashboardStatsOptions = {}) {
dashboardData.recentActivities = normalizeRecentActivities(
detail.recentActivities ?? detail.recent_activities ?? []
)
+ dashboardData.operationalInsights = normalizeOperationalInsights(
+ detail.operationalInsights ?? detail.operational_insights ?? detail.insights ?? []
+ )
dashboardData.storageUsedText = formatFileSize(dashboardData.storageUsed)
dashboardData.yesterdaySizeText = formatFileSize(dashboardData.yesterdaySize)
diff --git a/src/i18n/locales/en-US.ts b/src/i18n/locales/en-US.ts
index 76f62ea..9d11baa 100644
--- a/src/i18n/locales/en-US.ts
+++ b/src/i18n/locales/en-US.ts
@@ -90,6 +90,17 @@ export default {
textType: 'Text',
recentActivities: 'Recent Activity',
recentActivitiesDesc: 'Key admin changes for quick operational traceability.',
+ operationalInsightsTitle: 'Operational Insights',
+ operationalInsightsDesc:
+ 'Suggested next actions generated from file health, upload policy, and recent capacity.',
+ operationalInsightActionSettings: 'Adjust settings',
+ operationalInsightActionFileQueue: 'View queue',
+ operationalInsightSeverity: {
+ danger: 'High risk',
+ warning: 'Action needed',
+ success: 'Stable',
+ neutral: 'Suggestion'
+ },
viewAllActivities: 'View all',
activityTimelineTitle: 'Activity Timeline',
activityTimelineFiltered: 'Current Results',
@@ -132,6 +143,43 @@ export default {
local_file: 'Local file',
system: 'System'
},
+ operationalInsights: {
+ storage_issue: {
+ title: 'Fix {count} storage issues',
+ description:
+ 'Some records are missing download metadata. Review storage settings and file details first.'
+ },
+ expired_cleanup: {
+ title: 'Clean up {count} expired files',
+ description:
+ 'Expired records add management noise. Delete them in bulk or extend the files that should remain available.'
+ },
+ expiring_soon: {
+ title: '{count} files expiring soon',
+ description:
+ 'These shares will become unavailable soon. Extend them early or notify the recipients.'
+ },
+ never_retrieved: {
+ title: '{count} files never retrieved',
+ description:
+ 'Shares that were never retrieved can reveal ineffective uploads or follow-up gaps.'
+ },
+ guest_upload_retention: {
+ title: 'Guest uploads lack retention limits',
+ description:
+ 'Guest upload is enabled with unlimited retention. Set a default retention window to reduce storage pressure.'
+ },
+ chunking_disabled: {
+ title: 'Consider enabling chunked upload',
+ description:
+ "Today's added capacity is near the single-file limit. Chunked upload can improve large-file reliability."
+ },
+ healthy: {
+ title: 'Operations look stable',
+ description:
+ 'No high-priority operational risk was detected. Keep watching the file health trend.'
+ }
+ },
recentFiles: 'Recent Shares',
recentFilesDesc: 'Recently created share records for quick status checks.',
available: 'Available',
@@ -875,6 +923,17 @@ export default {
textType: 'Text',
recentActivities: 'Recent Activity',
recentActivitiesDesc: 'Key admin changes for quick operational traceability.',
+ operationalInsightsTitle: 'Operational Insights',
+ operationalInsightsDesc:
+ 'Suggested next actions generated from file health, upload policy, and recent capacity.',
+ operationalInsightActionSettings: 'Adjust settings',
+ operationalInsightActionFileQueue: 'View queue',
+ operationalInsightSeverity: {
+ danger: 'High risk',
+ warning: 'Action needed',
+ success: 'Stable',
+ neutral: 'Suggestion'
+ },
viewAllActivities: 'View all',
activityTimelineTitle: 'Activity Timeline',
activityTimelineFiltered: 'Current Results',
@@ -917,6 +976,43 @@ export default {
local_file: 'Local file',
system: 'System'
},
+ operationalInsights: {
+ storage_issue: {
+ title: 'Fix {count} storage issues',
+ description:
+ 'Some records are missing download metadata. Review storage settings and file details first.'
+ },
+ expired_cleanup: {
+ title: 'Clean up {count} expired files',
+ description:
+ 'Expired records add management noise. Delete them in bulk or extend the files that should remain available.'
+ },
+ expiring_soon: {
+ title: '{count} files expiring soon',
+ description:
+ 'These shares will become unavailable soon. Extend them early or notify the recipients.'
+ },
+ never_retrieved: {
+ title: '{count} files never retrieved',
+ description:
+ 'Shares that were never retrieved can reveal ineffective uploads or follow-up gaps.'
+ },
+ guest_upload_retention: {
+ title: 'Guest uploads lack retention limits',
+ description:
+ 'Guest upload is enabled with unlimited retention. Set a default retention window to reduce storage pressure.'
+ },
+ chunking_disabled: {
+ title: 'Consider enabling chunked upload',
+ description:
+ "Today's added capacity is near the single-file limit. Chunked upload can improve large-file reliability."
+ },
+ healthy: {
+ title: 'Operations look stable',
+ description:
+ 'No high-priority operational risk was detected. Keep watching the file health trend.'
+ }
+ },
recentFiles: 'Recent Shares',
recentFilesDesc: 'Recently created share records for quick status checks.',
available: 'Available',
diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts
index cee7f8a..583d9c3 100644
--- a/src/i18n/locales/zh-CN.ts
+++ b/src/i18n/locales/zh-CN.ts
@@ -90,6 +90,16 @@ export default {
textType: '文本',
recentActivities: '最近操作',
recentActivitiesDesc: '记录管理员最近的关键改动,便于回溯处理链路。',
+ operationalInsightsTitle: '运营建议',
+ operationalInsightsDesc: '根据文件健康、上传策略和近期容量自动生成下一步动作。',
+ operationalInsightActionSettings: '调整设置',
+ operationalInsightActionFileQueue: '查看队列',
+ operationalInsightSeverity: {
+ danger: '高风险',
+ warning: '需处理',
+ success: '稳定',
+ neutral: '建议'
+ },
viewAllActivities: '查看全部',
activityTimelineTitle: '操作时间线',
activityTimelineFiltered: '当前结果',
@@ -132,6 +142,36 @@ export default {
local_file: '本地文件',
system: '系统'
},
+ operationalInsights: {
+ storage_issue: {
+ title: '修复 {count} 个存储异常',
+ description: '部分记录缺少下载元数据,建议优先核对存储配置和文件详情。'
+ },
+ expired_cleanup: {
+ title: '清理 {count} 个已过期文件',
+ description: '已过期记录会占用管理视野,建议批量删除或续期仍需保留的文件。'
+ },
+ expiring_soon: {
+ title: '{count} 个文件即将过期',
+ description: '这些分享将在短时间内失效,适合提前续期或通知使用方。'
+ },
+ never_retrieved: {
+ title: '{count} 个文件未被取件',
+ description: '创建后长期无人取件的分享可用于判断无效上传或触达效果。'
+ },
+ guest_upload_retention: {
+ title: '游客上传缺少保留上限',
+ description: '游客上传已开启且保存时间不受限,建议设置默认保留期降低存储压力。'
+ },
+ chunking_disabled: {
+ title: '建议开启分片上传',
+ description: '今日新增容量已接近单文件上限,开启分片上传可提升大文件体验。'
+ },
+ healthy: {
+ title: '运行状态稳定',
+ description: '当前未发现优先级较高的运营风险,可继续观察文件健康趋势。'
+ }
+ },
recentFiles: '最近分享',
recentFilesDesc: '最近创建的分享记录,便于快速核对状态。',
available: '可取件',
@@ -837,6 +877,16 @@ export default {
textType: '文本',
recentActivities: '最近操作',
recentActivitiesDesc: '记录管理员最近的关键改动,便于回溯处理链路。',
+ operationalInsightsTitle: '运营建议',
+ operationalInsightsDesc: '根据文件健康、上传策略和近期容量自动生成下一步动作。',
+ operationalInsightActionSettings: '调整设置',
+ operationalInsightActionFileQueue: '查看队列',
+ operationalInsightSeverity: {
+ danger: '高风险',
+ warning: '需处理',
+ success: '稳定',
+ neutral: '建议'
+ },
viewAllActivities: '查看全部',
activityTimelineTitle: '操作时间线',
activityTimelineFiltered: '当前结果',
@@ -879,6 +929,36 @@ export default {
local_file: '本地文件',
system: '系统'
},
+ operationalInsights: {
+ storage_issue: {
+ title: '修复 {count} 个存储异常',
+ description: '部分记录缺少下载元数据,建议优先核对存储配置和文件详情。'
+ },
+ expired_cleanup: {
+ title: '清理 {count} 个已过期文件',
+ description: '已过期记录会占用管理视野,建议批量删除或续期仍需保留的文件。'
+ },
+ expiring_soon: {
+ title: '{count} 个文件即将过期',
+ description: '这些分享将在短时间内失效,适合提前续期或通知使用方。'
+ },
+ never_retrieved: {
+ title: '{count} 个文件未被取件',
+ description: '创建后长期无人取件的分享可用于判断无效上传或触达效果。'
+ },
+ guest_upload_retention: {
+ title: '游客上传缺少保留上限',
+ description: '游客上传已开启且保存时间不受限,建议设置默认保留期降低存储压力。'
+ },
+ chunking_disabled: {
+ title: '建议开启分片上传',
+ description: '今日新增容量已接近单文件上限,开启分片上传可提升大文件体验。'
+ },
+ healthy: {
+ title: '运行状态稳定',
+ description: '当前未发现优先级较高的运营风险,可继续观察文件健康趋势。'
+ }
+ },
recentFiles: '最近分享',
recentFilesDesc: '最近创建的分享记录,便于快速核对状态。',
available: '可取件',
diff --git a/src/types/dashboard.ts b/src/types/dashboard.ts
index e472ab3..f7b0e18 100644
--- a/src/types/dashboard.ts
+++ b/src/types/dashboard.ts
@@ -43,6 +43,9 @@ export interface DashboardData {
recentFiles?: DashboardRecentFile[]
recentActivities?: DashboardActivity[]
recent_activities?: DashboardActivity[]
+ operationalInsights?: DashboardOperationalInsight[]
+ operational_insights?: DashboardOperationalInsight[]
+ insights?: DashboardOperationalInsight[]
}
export interface DashboardSuffixStat {
@@ -120,6 +123,36 @@ export interface DashboardActivitiesResponse {
target_type_options?: DashboardActivityOption[]
}
+export type DashboardInsightSeverity = 'danger' | 'warning' | 'success' | 'neutral'
+
+export interface DashboardOperationalInsightAction {
+ type?: string
+ actionType?: string
+ action_type?: string
+ health?: AdminFileHealthFilter | string | null
+ targetHealth?: AdminFileHealthFilter | string | null
+ target_health?: AdminFileHealthFilter | string | null
+}
+
+export interface DashboardOperationalInsight {
+ key: string
+ severity: DashboardInsightSeverity | string
+ priority?: number
+ count: number
+ action?: DashboardOperationalInsightAction
+ actionType?: string
+ action_type?: string
+ targetHealth?: AdminFileHealthFilter | string | null
+ target_health?: AdminFileHealthFilter | string | null
+}
+
+export interface DashboardOperationalInsightViewItem extends DashboardOperationalInsight {
+ severity: DashboardInsightSeverity
+ priority: number
+ actionTypeValue: string
+ targetHealthValue: AdminFileHealthFilter | string | null
+}
+
export type DashboardViewData = Omit<
DashboardData,
| keyof DashboardHealthSummary
@@ -138,6 +171,9 @@ export type DashboardViewData = Omit<
| 'recentFiles'
| 'recentActivities'
| 'recent_activities'
+ | 'operationalInsights'
+ | 'operational_insights'
+ | 'insights'
| 'storageUsed'
| 'yesterdaySize'
| 'todaySize'
@@ -158,6 +194,7 @@ export type DashboardViewData = Omit<
topSuffixes: DashboardSuffixStat[]
recentFiles: DashboardRecentFile[]
recentActivities: DashboardActivityViewItem[]
+ operationalInsights: DashboardOperationalInsightViewItem[]
storageUsed: number
yesterdaySize: number
todaySize: number
diff --git a/src/views/manage/DashboardView.vue b/src/views/manage/DashboardView.vue
index 12842d7..c60233d 100644
--- a/src/views/manage/DashboardView.vue
+++ b/src/views/manage/DashboardView.vue
@@ -84,6 +84,56 @@
+
+ {{ t('admin.dashboard.operationalInsightsDesc') }}
+
+ {{ t('admin.dashboard.operationalInsightsTitle') }}
+
+