download link and analytics link for admin

This commit is contained in:
Michael Freno
2026-01-06 01:51:34 -05:00
parent ea556b3677
commit 3ce61bbe4c

View File

@@ -17,8 +17,46 @@ import { ActivityHeatmap } from "./ActivityHeatmap";
import { DarkModeToggle } from "./DarkModeToggle"; import { DarkModeToggle } from "./DarkModeToggle";
import { SkeletonBox, SkeletonText } from "./SkeletonLoader"; import { SkeletonBox, SkeletonText } from "./SkeletonLoader";
import { env } from "~/env/client"; import { env } from "~/env/client";
import { A, useNavigate, useLocation } from "@solidjs/router"; import {
A,
useNavigate,
useLocation,
query,
createAsync
} from "@solidjs/router";
import { BREAKPOINTS } from "~/config"; import { BREAKPOINTS } from "~/config";
import { getRequestEvent } from "solid-js/web";
const getUserState = query(async () => {
"use server";
const { getPrivilegeLevel, getUserID } = await import("~/server/utils");
const { ConnectionFactory } = await import("~/server/utils");
const event = getRequestEvent()!;
const privilegeLevel = await getPrivilegeLevel(event.nativeEvent);
const userId = await getUserID(event.nativeEvent);
if (!userId) {
return {
isAuthenticated: false,
email: null,
privilegeLevel: "anonymous" as const
};
}
const conn = ConnectionFactory();
const res = await conn.execute({
sql: "SELECT email FROM User WHERE id = ?",
args: [userId]
});
const email = res.rows[0] ? (res.rows[0].email as string | null) : null;
return {
isAuthenticated: true,
email,
privilegeLevel
};
}, "bars-user-state");
function formatDomainName(url: string): string { function formatDomainName(url: string): string {
const domain = url.split("://")[1]?.split(":")[0] ?? url; const domain = url.split("://")[1]?.split(":")[0] ?? url;
@@ -157,6 +195,26 @@ export function RightBarContent() {
<span>Resume</span> <span>Resume</span>
</a> </a>
</li> </li>
<li>
<a
href="/downloads"
onClick={handleLinkClick}
class="hover:text-subtext0 flex items-center gap-3 transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105"
>
<span class="shaker rounded-full p-2">
<svg
xmlns="http://www.w3.org/2000/svg"
height={24}
width={24}
viewBox="0 0 512 512"
class="fill-text"
>
<path d="M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V274.7l-73.4-73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 274.7V32zM64 352c-35.3 0-64 28.7-64 64v32c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V416c0-35.3-28.7-64-64-64H346.5l-45.3 45.3c-25 25-65.5 25-90.5 0L165.5 352H64zm368 56a24 24 0 1 1 0 48 24 24 0 1 1 0-48z" />
</svg>
</span>
<span>Downloads</span>
</a>
</li>
</ul> </ul>
</Typewriter> </Typewriter>
@@ -188,17 +246,13 @@ export function RightBarContent() {
export function LeftBar() { export function LeftBar() {
const { leftBarVisible, setLeftBarVisible } = useBars(); const { leftBarVisible, setLeftBarVisible } = useBars();
const location = useLocation(); const location = useLocation();
const userState = createAsync(() => getUserState());
let ref: HTMLDivElement | undefined; let ref: HTMLDivElement | undefined;
const [recentPosts, setRecentPosts] = createSignal<any[] | undefined>( const [recentPosts, setRecentPosts] = createSignal<any[] | undefined>(
undefined undefined
); );
const [userInfo, setUserInfo] = createSignal<{
email: string | null;
isAuthenticated: boolean;
} | null>(null);
const [isMounted, setIsMounted] = createSignal(false); const [isMounted, setIsMounted] = createSignal(false);
const [signOutLoading, setSignOutLoading] = createSignal(false); const [signOutLoading, setSignOutLoading] = createSignal(false);
const [getLostText, setGetLostText] = createSignal("What's this?"); const [getLostText, setGetLostText] = createSignal("What's this?");
@@ -229,31 +283,6 @@ export function LeftBar() {
} }
}; };
const fetchUserInfo = async () => {
try {
const response = await fetch("/api/trpc/user.getProfile", {
method: "GET"
});
if (response.ok) {
const result = await response.json();
if (result.result?.data) {
setUserInfo({
email: result.result.data.email,
isAuthenticated: true
});
} else {
setUserInfo({ email: null, isAuthenticated: false });
}
} else {
setUserInfo({ email: null, isAuthenticated: false });
}
} catch (error) {
console.error("Failed to fetch user info:", error);
setUserInfo({ email: null, isAuthenticated: false });
}
};
onMount(() => { onMount(() => {
setIsMounted(true); setIsMounted(true);
@@ -377,8 +406,6 @@ export function LeftBar() {
console.error("Failed to fetch recent posts:", error); console.error("Failed to fetch recent posts:", error);
setRecentPosts([]); setRecentPosts([]);
} }
await fetchUserInfo();
}; };
setTimeout(() => { setTimeout(() => {
@@ -386,14 +413,6 @@ export function LeftBar() {
}, 0); }, 0);
}); });
createEffect(() => {
location.pathname;
if (isMounted()) {
fetchUserInfo();
}
});
const navigate = useNavigate(); const navigate = useNavigate();
const getMainNavStyles = () => { const getMainNavStyles = () => {
const baseStyles = { const baseStyles = {
@@ -539,6 +558,15 @@ export function LeftBar() {
Blog Blog
</a> </a>
</li> </li>
<Show
when={isMounted() && userState()?.privilegeLevel === "admin"}
>
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
<a href="/analytics" onClick={handleLinkClick}>
Analytics
</a>
</li>
</Show>
<li <li
class="hover:text-subtext0 w-fit cursor-pointer transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold" class="hover:text-subtext0 w-fit cursor-pointer transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold"
onClick={() => { onClick={() => {
@@ -547,7 +575,7 @@ export function LeftBar() {
}} }}
> >
<Show <Show
when={isMounted() && userInfo()?.isAuthenticated} when={isMounted() && userState()?.isAuthenticated}
fallback={ fallback={
<a href="/login" onClick={handleLinkClick}> <a href="/login" onClick={handleLinkClick}>
Login Login
@@ -556,16 +584,16 @@ export function LeftBar() {
> >
<A href="/account" onClick={handleLinkClick}> <A href="/account" onClick={handleLinkClick}>
Account Account
<Show when={userInfo()?.email}> <Show when={userState()?.email}>
<span class="text-subtext0 text-sm font-normal"> <span class="text-subtext0 text-sm font-normal">
{" "} {" "}
({userInfo()!.email}) ({userState()!.email})
</span> </span>
</Show> </Show>
</A> </A>
</Show> </Show>
</li> </li>
<Show when={isMounted() && userInfo()?.isAuthenticated}> <Show when={isMounted() && userState()?.isAuthenticated}>
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold"> <li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
<button <button
onClick={handleSignOut} onClick={handleSignOut}