From 31187b1a742d500567d344cc9aeff1c20e18b551 Mon Sep 17 00:00:00 2001 From: Brian Choromanski Date: Fri, 17 Jul 2026 14:34:08 -0400 Subject: [PATCH] feat: Added sitewide statistics (#7322) --- frontend/app/lang/messages/en-US.json | 5 +- frontend/app/pages/admin/site-settings.vue | 82 +++++++++++++++++++++- 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/frontend/app/lang/messages/en-US.json b/frontend/app/lang/messages/en-US.json index c21f7450d..572be17c1 100644 --- a/frontend/app/lang/messages/en-US.json +++ b/frontend/app/lang/messages/en-US.json @@ -940,7 +940,10 @@ "openai-ready": "OpenAI Ready", "openai-not-ready": "OpenAI Not Ready", "openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.", - "openai-ready-success-text": "Required OpenAI variables are all set." + "openai-ready-success-text": "Required OpenAI variables are all set.", + "site-statistics": "Site Statistics", + "uncategorized-recipes": "Uncategorized Recipes", + "untagged-recipes": "Untagged Recipes" }, "shopping-list": { "all-lists": "All Lists", diff --git a/frontend/app/pages/admin/site-settings.vue b/frontend/app/pages/admin/site-settings.vue index db4425893..4438d9708 100644 --- a/frontend/app/pages/admin/site-settings.vue +++ b/frontend/app/pages/admin/site-settings.vue @@ -157,6 +157,34 @@ + +
+ +
+ + + + +
+
+
({ ldapReady: false, oidcReady: false, }); +const adminStats = ref({ + totalRecipes: 0, + totalUsers: 0, + totalHouseholds: 0, + totalGroups: 0, + uncategorizedRecipes: 0, + untaggedRecipes: 0, +}); function isLocalHostOrHttps() { return window.location.hostname === "localhost" || window.location.protocol === "https:"; } const api = useUserApi(); const adminApi = useAdminApi(); + +const adminStatsText: { [key: string]: string } = { + totalRecipes: i18n.t("general.recipes"), + totalUsers: i18n.t("user.users"), + totalHouseholds: i18n.t("household.households"), + totalGroups: i18n.t("group.groups"), + uncategorizedRecipes: i18n.t("settings.uncategorized-recipes"), + untaggedRecipes: i18n.t("settings.untagged-recipes"), +}; + +function getAdminStatsTitle(key: string) { + return adminStatsText[key] ?? key; +} + +const adminStatsIcon: { [key: string]: string } = { + totalRecipes: $globals.icons.primary, + totalUsers: $globals.icons.user, + totalHouseholds: $globals.icons.household, + totalGroups: $globals.icons.group, + uncategorizedRecipes: $globals.icons.categories, + untaggedRecipes: $globals.icons.tags, +}; + +function getAdminStatsIcon(key: string) { + return adminStatsIcon[key] ?? $globals.icons.primary; +} + +const adminStatsTo = computed<{ [key: string]: string }>(() => { + return { + totalUsers: "/admin/manage/users", + totalHouseholds: "/admin/manage/households", + totalGroups: "/admin/manage/groups", + }; +}); + +function getAdminStatsTo(key: string) { + return adminStatsTo.value[key] ?? undefined; +} + onMounted(async () => { const { data } = await adminApi.about.checkApp(); if (data) { appConfig.value = { ...data, isSiteSecure: false }; } appConfig.value.isSiteSecure = isLocalHostOrHttps(); + const { data: adminData } = await adminApi.about.statistics(); + if (adminData) { + adminStats.value = adminData; + } }); const simpleChecks = computed(() => { const goodIcon = $globals.icons.checkboxMarkedCircle;