Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 86 additions & 43 deletions apps/admin/src/components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Link } from "@tanstack/react-router";
import {
Activity,
BookOpen,
ChevronDown,
ExternalLink,
FileText,
Hash,
Expand All @@ -14,6 +15,7 @@ import {
Users,
Zap,
} from "lucide-react";
import { Accordion } from "radix-ui";
import { useAccount } from "../features/auth/hooks/useAccount";
import { clearTokens } from "../utils/api";

Expand All @@ -35,6 +37,31 @@ const MenuItem = ({ icon, label, to }: MenuItemProps) => {
);
};

interface MenuGroupProps {
value: string;
label: string;
children: React.ReactNode;
}

const MenuGroup = ({ value, label, children }: MenuGroupProps) => {
return (
<Accordion.Item value={value} className="border-none">
<Accordion.Header>
<Accordion.Trigger className="group flex w-full items-center justify-between rounded-lg p-2 font-medium text-foreground/70 text-sm transition duration-150 hover:bg-primary hover:text-foreground">
<span>{label}</span>
<ChevronDown
size={16}
className="transition-transform duration-200 group-data-[state=open]:rotate-180"
/>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content className="overflow-hidden data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down">
<div className="ml-2 space-y-1 pt-1">{children}</div>
</Accordion.Content>
</Accordion.Item>
);
};

export const Sidebar = () => {
const { account } = useAccount();

Expand All @@ -54,49 +81,65 @@ export const Sidebar = () => {
<h2 className="font-medium">Opencircle</h2>
</Link>
</section>
<nav className="mt-8 space-y-2">
<MenuItem
icon={<Activity size={20} />}
label="Activity"
to="/activity"
/>
<MenuItem icon={<Users size={20} />} label="Users" to="/users" />
<MenuItem icon={<Hash size={20} />} label="Channels" to="/channels" />
<MenuItem
icon={<Key size={20} />}
label="Invite Codes"
to="/invite-codes"
/>
<MenuItem
icon={<BookOpen size={20} />}
label="Courses"
to="/courses"
/>
<MenuItem
icon={<UserCheck size={20} />}
label="Enrollments"
to="/enrollments"
/>
<MenuItem
icon={<FileText size={20} />}
label="Articles"
to="/articles"
/>
<MenuItem
icon={<LinkIcon size={20} />}
label="Resources"
to="/resources"
/>
<MenuItem
icon={<ExternalLink size={20} />}
label="App Links"
to="/app-links"
/>
<MenuItem
icon={<Settings size={20} />}
label="App Settings"
to="/app-settings"
/>
<nav className="mt-8">
<Accordion.Root
type="multiple"
defaultValue={["community", "content", "settings"]}
className="space-y-2"
>
<MenuGroup value="community" label="Community">
<MenuItem
icon={<Activity size={18} />}
label="Activity"
to="/activity"
/>
<MenuItem icon={<Users size={18} />} label="Users" to="/users" />
<MenuItem
icon={<Hash size={18} />}
label="Channels"
to="/channels"
/>
<MenuItem
icon={<Key size={18} />}
label="Invite Codes"
to="/invite-codes"
/>
</MenuGroup>
<MenuGroup value="content" label="Content">
<MenuItem
icon={<BookOpen size={18} />}
label="Courses"
to="/courses"
/>
<MenuItem
icon={<UserCheck size={18} />}
label="Enrollments"
to="/enrollments"
/>
<MenuItem
icon={<FileText size={18} />}
label="Articles"
to="/articles"
/>
<MenuItem
icon={<LinkIcon size={18} />}
label="Resources"
to="/resources"
/>
</MenuGroup>
<MenuGroup value="settings" label="Settings">
<MenuItem
icon={<ExternalLink size={18} />}
label="App Links"
to="/app-links"
/>
<MenuItem
icon={<Settings size={18} />}
label="App Settings"
to="/app-settings"
/>
</MenuGroup>
</Accordion.Root>
</nav>
<section className="mt-8 p-2">
{account && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const CourseEnrollmentChart = ({
const hasData = chartData.some((item) => item.enrollments > 0);

return (
<div className="rounded-lg bg-background-secondary p-6 shadow">
<div className="rounded-lg bg-background-secondary p-6 shadow outline-none focus:outline-none">
<h3 className="mb-4 font-semibold text-foreground text-lg">
Course Enrollment Distribution
</h3>
Expand Down
2 changes: 1 addition & 1 deletion apps/admin/src/features/dashboard/userGrowthChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const UserGrowthChart = ({ data, isLoading }: UserGrowthChartProps) => {
const hasData = chartData.length > 0;

return (
<div className="rounded-lg bg-background-secondary p-6 shadow">
<div className="rounded-lg bg-background-secondary p-6 shadow outline-none focus:outline-none">
<h3 className="mb-4 font-semibold text-foreground text-lg">
User Growth Over Time
</h3>
Expand Down
6 changes: 6 additions & 0 deletions apps/admin/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@
height: 100%;
margin-bottom: 1px;
}

.recharts-wrapper,
.recharts-wrapper svg,
.recharts-surface {
outline: none !important;
}
}
119 changes: 66 additions & 53 deletions apps/admin/src/routes/_dashboardLayout/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type {
DashboardStats,
UserGrowthData,
} from "@opencircle/core";
import { Button } from "@opencircle/ui";
import { useQuery } from "@tanstack/react-query";
import { createFileRoute, Link } from "@tanstack/react-router";
import { format } from "date-fns";
Expand Down Expand Up @@ -251,7 +250,7 @@ function RouteComponent() {
</div>

{/* Activity Chart */}
<div className="rounded-lg bg-background-secondary p-6 shadow">
<div className="rounded-lg bg-background-secondary p-6 shadow outline-none focus:outline-none">
<div className="mb-4 flex items-center justify-between">
<h3 className="font-semibold text-foreground text-lg">
7-Day Activity Overview
Expand Down Expand Up @@ -347,91 +346,105 @@ function RouteComponent() {
/>
</div>

{/* Quick Stats & Links */}
{/* Quick Stats & Actions */}
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
{/* Activity Stats */}
<div className="rounded-lg bg-background-secondary p-6 shadow">
<div className="rounded-lg border border-border bg-background-secondary p-6 shadow">
<h3 className="mb-4 font-semibold text-foreground text-lg">
Activity Stats
</h3>
<div className="space-y-3">
<div className="flex justify-between">
<span className="text-foreground-secondary">Total Sessions</span>
<span className="font-medium text-foreground">
<div className="grid grid-cols-2 gap-3">
<div className="rounded-lg border border-border bg-background p-4 text-center">
<p className="font-bold text-2xl text-foreground">
{activityStats?.total_sessions || 0}
</span>
</p>
<p className="mt-1 text-foreground-secondary text-xs">
Total Sessions
</p>
</div>
<div className="flex justify-between">
<span className="text-foreground-secondary">Avg Duration</span>
<span className="font-medium text-foreground">
<div className="rounded-lg border border-border bg-background p-4 text-center">
<p className="font-bold text-2xl text-foreground">
{Math.round(activityStats?.average_duration_seconds || 0)}s
</span>
</p>
<p className="mt-1 text-foreground-secondary text-xs">
Avg Duration
</p>
</div>
<div className="flex justify-between">
<span className="text-foreground-secondary">Unique Users</span>
<span className="font-medium text-foreground">
<div className="col-span-2 rounded-lg border border-border bg-background p-4 text-center">
<p className="font-bold text-2xl text-foreground">
{activityStats?.unique_users || 0}
</span>
</p>
<p className="mt-1 text-foreground-secondary text-xs">
Unique Users
</p>
</div>
</div>
</div>

{/* Content Breakdown */}
<div className="rounded-lg bg-background-secondary p-6 shadow">
<div className="rounded-lg border border-border bg-background-secondary p-6 shadow">
<h3 className="mb-4 font-semibold text-foreground text-lg">
Content Breakdown
</h3>
<div className="space-y-3">
<div className="flex justify-between">
<span className="text-foreground-secondary">Articles</span>
<span className="font-medium text-foreground">
<div className="grid grid-cols-2 gap-3">
<div className="rounded-lg border border-border bg-background p-4 text-center">
<p className="font-bold text-2xl text-foreground">
{articles.length}
</span>
</p>
<p className="mt-1 text-foreground-secondary text-xs">Articles</p>
</div>
<div className="flex justify-between">
<span className="text-foreground-secondary">Resources</span>
<span className="font-medium text-foreground">
<div className="rounded-lg border border-border bg-background p-4 text-center">
<p className="font-bold text-2xl text-foreground">
{resources.length}
</span>
</p>
<p className="mt-1 text-foreground-secondary text-xs">
Resources
</p>
</div>
<div className="flex justify-between">
<span className="text-foreground-secondary">Total Courses</span>
<span className="font-medium text-foreground">
<div className="col-span-2 rounded-lg border border-border bg-background p-4 text-center">
<p className="font-bold text-2xl text-foreground">
{dashboardStats?.totalCourses || 0}
</span>
</p>
<p className="mt-1 text-foreground-secondary text-xs">
Total Courses
</p>
</div>
</div>
</div>

{/* Quick Actions */}
<div className="rounded-lg bg-background-secondary p-6 shadow">
<div className="rounded-lg border border-border bg-background-secondary p-6 shadow">
<h3 className="mb-4 font-semibold text-foreground text-lg">
Quick Actions
</h3>
<div className="space-y-2">
<Link to="/courses/new" className="block">
<Button variant="secondary" className="w-full justify-start">
<BookOpen size={16} />
<span>Create Course</span>
</Button>
<div className="grid grid-cols-2 gap-3">
<Link
to="/courses/new"
className="flex flex-col items-center gap-2 rounded-lg border border-border bg-background p-4 transition hover:border-primary hover:bg-primary/5"
>
<BookOpen size={20} className="text-foreground" />
<span className="text-foreground text-xs">New Course</span>
</Link>
<Link to="/channels" className="block">
<Button variant="secondary" className="w-full justify-start">
<Hash size={16} />
<span>Manage Channels</span>
</Button>
<Link
to="/channels"
className="flex flex-col items-center gap-2 rounded-lg border border-border bg-background p-4 transition hover:border-primary hover:bg-primary/5"
>
<Hash size={20} className="text-foreground" />
<span className="text-foreground text-xs">Channels</span>
</Link>
<Link to="/invite-codes/new" className="block">
<Button variant="secondary" className="w-full justify-start">
<Zap size={16} />
<span>Generate Invite</span>
</Button>
<Link
to="/invite-codes/new"
className="flex flex-col items-center gap-2 rounded-lg border border-border bg-background p-4 transition hover:border-primary hover:bg-primary/5"
>
<Zap size={20} className="text-foreground" />
<span className="text-foreground text-xs">Invite Code</span>
</Link>
<Link to="/app-settings" className="block">
<Button variant="secondary" className="w-full justify-start">
<LinkIcon size={16} />
<span>App Settings</span>
</Button>
<Link
to="/app-settings"
className="flex flex-col items-center gap-2 rounded-lg border border-border bg-background p-4 transition hover:border-primary hover:bg-primary/5"
>
<LinkIcon size={20} className="text-foreground" />
<span className="text-foreground text-xs">Settings</span>
</Link>
</div>
</div>
Expand Down