diff --git a/backend/src/routes/admin.ts b/backend/src/routes/admin.ts
index aeb9266..4bece27 100644
--- a/backend/src/routes/admin.ts
+++ b/backend/src/routes/admin.ts
@@ -36,6 +36,20 @@ export async function adminRoutes(app: FastifyInstance) {
return { total: pending.length, indexed: done, no_text_found: failed };
});
+ /**
+ * GET /api/admin/stats
+ * Aggregate share and view counts across all memes.
+ */
+ app.get('/api/admin/stats', { preHandler: requireAuth }, async () => {
+ const row = db
+ .prepare('SELECT SUM(share_count) as total_shares, SUM(view_count) as total_views FROM memes')
+ .get() as { total_shares: number | null; total_views: number | null };
+ return {
+ total_shares: row.total_shares ?? 0,
+ total_views: row.total_views ?? 0,
+ };
+ });
+
/**
* GET /api/admin/reindex/status
* Returns how many memes still need OCR indexing.
diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts
index 126185f..6d7bb42 100644
--- a/frontend/src/api/client.ts
+++ b/frontend/src/api/client.ts
@@ -155,6 +155,10 @@ export const api = {
reindex(): Promise<{ total: number; indexed: number; no_text_found: number }> {
return apiFetch('/api/admin/reindex', { method: 'POST' });
},
+
+ stats(): Promise<{ total_shares: number; total_views: number }> {
+ return apiFetch('/api/admin/stats');
+ },
},
imageUrl(filePath: string): string {
diff --git a/frontend/src/components/SettingsModal.tsx b/frontend/src/components/SettingsModal.tsx
index 4df23a8..adca9c5 100644
--- a/frontend/src/components/SettingsModal.tsx
+++ b/frontend/src/components/SettingsModal.tsx
@@ -1,5 +1,6 @@
-import { X, ScanText, Database, RefreshCw, CheckCircle2, AlertCircle, Loader2 } from 'lucide-react';
-import { useReindexStatus, useReindex, useCollections, useTags, useMemes } from '../hooks/useMemes';
+import React from 'react';
+import { X, ScanText, Database, RefreshCw, CheckCircle2, AlertCircle, Loader2, Share2, MousePointerClick } from 'lucide-react';
+import { useReindexStatus, useReindex, useCollections, useTags, useMemes, useAdminStats } from '../hooks/useMemes';
interface Props {
onClose: () => void;
@@ -11,6 +12,7 @@ export function SettingsModal({ onClose }: Props) {
const { data: collections } = useCollections();
const { data: tags } = useTags();
const { data: allMemes } = useMemes({ parent_only: false, limit: 1 });
+ const { data: adminStats } = useAdminStats();
async function handleReindex() {
await reindex.mutateAsync();
@@ -45,6 +47,18 @@ export function SettingsModal({ onClose }: Props) {