diff --git a/frontend/index.html b/frontend/index.html index 4777785..e095f09 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,7 +2,8 @@ - + + Delta CMS diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..6a861b9 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/frontend/src/assets/icon.svg b/frontend/src/assets/icon.svg new file mode 100644 index 0000000..db57385 --- /dev/null +++ b/frontend/src/assets/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/logo.svg b/frontend/src/assets/logo.svg new file mode 100644 index 0000000..ab05121 --- /dev/null +++ b/frontend/src/assets/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 997bc9c..fd94f24 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -23,7 +23,8 @@ import { } from "lucide-react" import { cn } from "@/lib/utils" import { Separator } from "@/components/ui/separator" -import logo from "../assets/logo.png" +import logo from "../assets/logo.svg" +import icon from "../assets/icon.svg" import { useCurrentUserRole } from "../hooks/useCurrentUserRole" import { useApiFetch } from "../hooks/useApiFetch" import { useSessionAuth } from "../auth/sessionAuthContext" @@ -158,7 +159,11 @@ export default function Sidebar() { collapsed && "justify-center px-0", )} > - Delta + {collapsed ? ( + Delta CMS + ) : ( + Delta CMS + )} {/* {!collapsed && ( Delta CMS )} */} diff --git a/frontend/src/pages/activityView.tsx b/frontend/src/pages/activityView.tsx index 0416362..f1ad871 100644 --- a/frontend/src/pages/activityView.tsx +++ b/frontend/src/pages/activityView.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from "react" +import { useCallback, useEffect, useMemo, useState } from "react" import { Search, FileText, Tag, Settings, UserPlus, Users, RefreshCcw, Newspaper, ListTodo, Image, Upload, Trash2, MessageSquare } from "lucide-react" import { useApiFetch } from "../hooks/useApiFetch" @@ -69,6 +69,8 @@ const AVATAR_COLORS = [ "bg-rose-500", "bg-teal-500", "bg-indigo-500", "bg-amber-500", ] +const TOP_CONTRIBUTOR_LIMIT = 5 + function initials(name: string) { return name.split(" ").map((n) => n[0]).join("").slice(0, 2).toUpperCase() } @@ -101,8 +103,15 @@ export default function ActivityView() { .finally(() => setIsLoading(false)) }, [apiFetch]) + // First-appearance order. This is what pins a person to an avatar color, so + // it deliberately stays unsorted — reordering it would recolor the feed. const users = useMemo(() => [...new Set(events.map((event) => event.user || "System"))], [events]) + const avatarColor = useCallback( + (user: string) => AVATAR_COLORS[users.indexOf(user) % AVATAR_COLORS.length], + [users], + ) + const filtered = useMemo(() => events.filter((event) => { const meta = actionMeta(event.action) const query = search.toLowerCase() @@ -126,6 +135,20 @@ export default function ActivityView() { .sort((a, b) => b.count - a.count || a.meta.label.localeCompare(b.meta.label)) }, [events]) + // Busiest first and capped, so the card ranks people instead of just + // mirroring the order names happened to show up in the feed. + const topContributors = useMemo(() => { + const counts = new Map() + for (const event of events) { + const user = event.user || "System" + counts.set(user, (counts.get(user) ?? 0) + 1) + } + return [...counts.entries()] + .map(([user, count]) => ({ user, count })) + .sort((a, b) => b.count - a.count || a.user.localeCompare(b.user)) + .slice(0, TOP_CONTRIBUTOR_LIMIT) + }, [events]) + return (
@@ -164,10 +187,9 @@ export default function ActivityView() { const meta = actionMeta(event.action) const Icon = meta.icon const user = event.user || "System" - const userIdx = users.indexOf(user) return (
-
+
{initials(user)}
@@ -192,18 +214,19 @@ export default function ActivityView() {

Top Contributors

- {users.map((user, i) => { - const count = events.filter((event) => (event.user || "System") === user).length - return ( + {topContributors.length === 0 ? ( + No contributors yet. + ) : ( + topContributors.map(({ user, count }) => (
-
+
{initials(user)}
{user.split(" ")[0]} {count}
- ) - })} + )) + )}