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
14 changes: 13 additions & 1 deletion app/api/admin/stats/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ export async function GET() {
user_id: true,
total_seconds: true,
categories: true,
user: { select: { email: true } },
user: {
select: {
email: true,
name: true,
role: true,
email_verified: true,
wakatime_api_key: true,
},
},
},
}),
prisma.conversation.count(),
Expand All @@ -36,6 +44,10 @@ export async function GET() {
const users = topUserStats.map((row) => ({
user_id: row.user_id,
email: row.user.email,
name: row.user.name,
role: row.user.role,
email_verified: row.user.email_verified ? "Yes" : "No",
wakatime_api_key: row.user.wakatime_api_key ? "Yes" : "No",
total_seconds: Number(row.total_seconds),
categories: row.categories,
}));
Expand Down
10 changes: 5 additions & 5 deletions app/components/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -596,9 +596,9 @@ export default function Chat({ user }: { user: ChatUserShape }) {
isDraggingOver
? "border-blue-500/60 bg-blue-50 shadow-lg shadow-blue-500/10"
: "border-gray-200 bg-gray-50 hover:border-gray-300 focus-within:border-blue-500/50 focus-within:bg-white focus-within:shadow-[0_0_15px_rgba(99,102,241,0.1)]"
} transition-all duration-300 rounded-xl border flex items-end gap-2 p-2`}
} transition-all duration-300 rounded-xl border flex items-end gap-2`}
>
<button
{/*<button
onClick={() => fileInputRef.current?.click()}
disabled={isSendingMessage}
className="w-10 h-10 mb-[2px] rounded-full bg-transparent hover:bg-gray-100 flex items-center justify-center transition-all duration-300 flex-shrink-0 group"
Expand All @@ -607,7 +607,7 @@ export default function Chat({ user }: { user: ChatUserShape }) {
<div className="w-[28px] h-[28px] rounded-full bg-gray-50 flex items-center justify-center group-hover:bg-blue-500/20 group-hover:text-blue-600 text-gray-500 transition-colors">
<FontAwesomeIcon icon={faPlus} className="w-3.5 h-3.5" />
</div>
</button>
</button>*/}
<input
type="file"
ref={fileInputRef}
Expand All @@ -622,7 +622,7 @@ export default function Chat({ user }: { user: ChatUserShape }) {
value={input}
onChange={handleInputChange}
onKeyDown={handleInputKeyDown}
className="w-full block outline-none resize-none overflow-y-auto bg-transparent text-gray-700 placeholder:text-gray-500/80 leading-relaxed max-h-[150px] text-[15px] pt-0 pb-0 [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-gray-200 [&::-webkit-scrollbar-thumb]:rounded-full hover:[&::-webkit-scrollbar-thumb]:bg-white/30"
className="px-2 w-full block outline-none resize-none overflow-y-auto bg-transparent text-gray-700 placeholder:text-gray-500/80 leading-relaxed max-h-[150px] text-[15px] pt-0 pb-0 [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-gray-200 [&::-webkit-scrollbar-thumb]:rounded-full hover:[&::-webkit-scrollbar-thumb]:bg-white/30"
placeholder="Type a message..."
rows={1}
/>
Expand All @@ -632,7 +632,7 @@ export default function Chat({ user }: { user: ChatUserShape }) {
<button
onClick={sendMessage}
disabled={!canSendMessage}
className={`h-10 px-5 rounded-[20px] font-semibold text-[14px] flex items-center gap-2.5 transition-all duration-300 flex-shrink-0
className={`h-10 px-5 rounded-full font-semibold text-[14px] flex items-center gap-2.5 transition-all duration-300 flex-shrink-0
${
canSendMessage
? "bg-blue-500 text-white shadow-md shadow-blue-500/25"
Expand Down
6 changes: 0 additions & 6 deletions app/components/admin/Dashbord.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,6 @@ export default function Dashboard() {

return (
<div className="p-6 md:p-8 space-y-6">
<div className="flex flex-row justify-between items-center w-full">
<div>
<h1 className="text-3xl font-bold text-blue-600">Admin Panel</h1>
</div>
</div>

<TopInsights
totalUsers={totalUsers}
totalSeconds={totalSeconds}
Expand Down
4 changes: 2 additions & 2 deletions app/components/admin/Widgets/FeatureInsights.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default function FeatureInsights({
}) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div className="p-4 rounded-2xl bg-zinc-900 border border-zinc-800">
<div className="glass-card p-8">
<p className="text-sm text-gray-500 mb-2">Leaderboard Stats</p>
<div className="space-y-1 text-sm">
<div className="flex justify-between">
Expand All @@ -28,7 +28,7 @@ export default function FeatureInsights({
</div>
</div>

<div className="p-4 rounded-2xl bg-zinc-900 border border-zinc-800">
<div className="glass-card p-8">
<p className="text-sm text-gray-500 mb-2">Flex Stats</p>
<div className="space-y-1 text-sm">
<div className="flex justify-between">
Expand Down
16 changes: 9 additions & 7 deletions app/components/admin/Widgets/RankingInsights.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface CoderStats {
email: string;
name: string;
total_seconds: number;
}

Expand All @@ -11,6 +12,7 @@ export interface CategoryStat {

export interface AICoderStat {
email: string;
name: string;
aiTotalSeconds: number;
}

Expand All @@ -27,13 +29,13 @@ export default function RankingInsights({
}) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
<div className="p-4 rounded-2xl bg-zinc-900 border border-zinc-800">
<div className="glass-card p-8">
<p className="text-sm text-gray-500 mb-2">Top Coders</p>
<div className="space-y-1">
{top3.map((u, i) => (
<div key={i} className="flex justify-between text-sm">
<span className="truncate">
#{i + 1} {u.email}
#{i + 1} {u.name}
</span>
<span className="whitespace-nowrap truncate">
{Math.floor((u.total_seconds || 0) / 3600)} hrs
Expand All @@ -43,13 +45,13 @@ export default function RankingInsights({
</div>
</div>

<div className="p-4 rounded-2xl bg-zinc-900 border border-zinc-800">
<div className="glass-card p-8">
<p className="text-sm text-gray-500 mb-2">Least Coders</p>
<div className="space-y-1">
{bottom3.map((u, i) => (
<div key={i} className="flex justify-between text-sm">
<span className="truncate">
#{i + 1} {u.email}
#{i + 1} {u.name}
</span>
<span className="whitespace-nowrap truncate">
{Math.floor((u.total_seconds || 0) / 3600)} hrs
Expand All @@ -59,7 +61,7 @@ export default function RankingInsights({
</div>
</div>

<div className="p-4 rounded-2xl bg-zinc-900 border border-zinc-800">
<div className="glass-card p-8">
<p className="text-sm text-gray-500 mb-2">Category Stats</p>

<div className="space-y-1 text-sm">
Expand All @@ -74,13 +76,13 @@ export default function RankingInsights({
</div>
</div>

<div className="p-4 rounded-2xl bg-zinc-900 border border-zinc-800">
<div className="glass-card p-8">
<p className="text-sm text-gray-500 mb-2">Vibe Coders</p>

<div className="space-y-1 text-sm">
{aiCoders.map((c, i) => (
<div key={i} className="flex justify-between">
<span>{c.email}</span>
<span>{c.name}</span>
<span>{Math.floor(c.aiTotalSeconds / 3600)} hrs</span>
</div>
))}
Expand Down
8 changes: 4 additions & 4 deletions app/components/admin/Widgets/TopInsights.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export default function TopInsights({
}) {
return (
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
<div className="p-4 rounded-2xl bg-zinc-900 border border-zinc-800">
<div className="glass-card p-8">
<p className="text-sm text-gray-500">Total Users</p>
<h2 className="text-2xl font-bold">{totalUsers}</h2>
<p className="text-xs text-gray-500">
(Average: {Math.floor(totalUsers / 30)} users/day)
</p>
</div>

<div className="p-4 rounded-2xl bg-zinc-900 border border-zinc-800">
<div className="glass-card p-8">
<p className="text-sm text-gray-500">Total Coding Time</p>
<h2 className="text-2xl font-bold">
{Math.floor(totalSeconds / 3600)} hrs
Expand All @@ -29,15 +29,15 @@ export default function TopInsights({
</p>
</div>

<div className="p-4 rounded-2xl bg-zinc-900 border border-zinc-800">
<div className="glass-card p-8">
<p className="text-sm text-gray-500">Total Threads</p>
<h2 className="text-2xl font-bold">{totalThreads}</h2>
<p className="text-xs text-gray-500">
(Average: {Math.floor(totalThreads / 30)} threads/day)
</p>
</div>

<div className="p-4 rounded-2xl bg-zinc-900 border border-zinc-800">
<div className="glass-card p-8">
<p className="text-sm text-gray-500">Total Messages</p>
<h2 className="text-2xl font-bold">{totalMessages}</h2>
<p className="text-xs text-gray-500">
Expand Down
27 changes: 17 additions & 10 deletions app/components/admin/Widgets/UserLists.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export interface UserStat {
user_id: string | null;
email: string | null;
user_id: string;
name: string;
email: string;
role: string;
email_verified: string;
wakatime_api_key: string;
total_seconds: number | null;
categories: unknown;
}
Expand All @@ -13,29 +17,32 @@ export default function UserLists({
loading: boolean;
}) {
return (
<div className="overflow-x-auto rounded-2xl border border-zinc-800">
<div className="overflow-x-auto glass-card">
<table className="min-w-full text-sm">
<thead className="bg-zinc-900 text-gray-500">
<thead className="text-gray-500">
<tr>
<th className="text-left p-3 whitespace-nowrap">User</th>
<th className="text-left p-3 whitespace-nowrap">Name</th>
<th className="text-left p-3 whitespace-nowrap">Email</th>
<th className="text-left p-3 whitespace-nowrap">
Total Time (hrs)
</th>
<th className="text-left p-3 whitespace-nowrap">Role</th>
<th className="text-left p-3 whitespace-nowrap">Verified</th>
<th className="text-left p-3 whitespace-nowrap">WakaTime</th>
<th className="text-left p-3 whitespace-nowrap">Action</th>
</tr>
</thead>
<tbody>
{users.map((u, i) => (
<tr
key={i}
className="border-t border-zinc-800 hover:bg-zinc-900/50"
>
<td className="p-3">{u.user_id || "N/A"}</td>
<tr key={i} className="hover:bg-zinc-100">
<td className="p-3">{u.name || "N/A"}</td>
<td className="p-3">{u.email || "N/A"}</td>
<td className="p-3">
{Math.floor((u.total_seconds || 0) / 3600)}
</td>
<td className="p-3 capitalize">{u.role || "N/A"}</td>
<td className="p-3">{u.email_verified}</td>
<td className="p-3">{u.wakatime_api_key}</td>
<td></td>
</tr>
))}
Expand Down
1 change: 0 additions & 1 deletion app/components/auth/form/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ export default function LoginForm() {

toast.promise(loginPromise, {
pending: "Logging in...",
success: "Login successful! Redirecting...",
error: {
render({ data }) {
const err = data as Error;
Expand Down
29 changes: 0 additions & 29 deletions app/components/chat/Messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ function MessageRow({
);
const senderRow = conversationRow?.users.find((u) => u.id === msg.sender_id);

const senderInitial = senderRow?.email?.[0]?.toUpperCase() ?? "?";
const senderName = senderRow?.email?.split("@")?.[0] ?? "";
const canOpenPrivateChat =
!isSelf && conversationRow?.type === "global" && !!senderRow?.email;
Expand All @@ -160,9 +159,6 @@ function MessageRow({
const text = msg.text ?? "";
const hasMedia = !!msg.attachments?.length;
const isLongMessage = hasMedia || text.length >= 120 || text.includes("\n");
const avatarTranslateClass = isLongMessage
? "-translate-y-[4.5px]"
: "-translate-y-[4px]";

const normalizedAttachments = (msg.attachments ?? [])
.map(normalizeAttachment)
Expand All @@ -184,31 +180,6 @@ function MessageRow({
isSelf ? "justify-end" : "justify-start"
}`}
>
{!isSelf && (
<div
role={canOpenPrivateChat ? "button" : undefined}
tabIndex={canOpenPrivateChat ? 0 : undefined}
onClick={handleProfileClick}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
handleProfileClick();
}}
title={canOpenPrivateChat ? "Start private chat" : undefined}
className={`flex-shrink-0 ${avatarTranslateClass} w-8 h-8 rounded-full bg-neutral-700 border border-gray-200 flex items-center justify-center aspect-square overflow-hidden ${
showHeader ? "" : "invisible"
} ${
canOpenPrivateChat
? "cursor-pointer hover:border-blue-400/60 hover:bg-neutral-700/80"
: ""
}`}
>
<span className="text-xs font-semibold leading-none text-gray-700">
{senderInitial}
</span>
</div>
)}

<div
className={`flex flex-col min-w-0 w-full max-w-[680px] ${
isSelf ? "items-end" : "items-start"
Expand Down
1 change: 1 addition & 0 deletions app/components/dashboard/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const navItems: NavItem[] = [
// Primary items shown directly in the bottom nav (mobile).
// Keep this to 4 so the "More" button fits as the 5th slot.
const BOTTOM_NAV_HREFS = ["/d", "/d/chat", "/d/kanban", "/d/leaderboards"];
const HIDDEN_BOTTOM_NAV_HREFS = ["/d/chat"];

export default function DashboardLayout({
email,
Expand Down
2 changes: 1 addition & 1 deletion app/d/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Metadata } from "next/types";
import { redirect } from "next/navigation";

export const metadata: Metadata = {
title: "Admin Panel - Devpulse",
title: "Admin Dashboard - Devpulse",
};

export default async function AdminPage() {
Expand Down
14 changes: 11 additions & 3 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,17 @@ body {
.grid-bg {
background-image:
linear-gradient(rgba(0, 0, 0, 0.04) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 0, 0, 0.04) 1px, transparent 1px);

background-size: 40px 40px;
linear-gradient(90deg, rgba(0, 0, 0, 0.04) 1px, transparent 1px),
radial-gradient(
circle at center,
transparent 0%,
rgba(255, 255, 255, 0.9) 80%
);

background-size:
40px 40px,
40px 40px,
100% 100%;
}

/* Decorative orb disabled */
Expand Down