feat: wire frontend pages to tRPC APIs
- Add hooks (useAuth, useSubscription, useNotifications) for real API data - Add auth service (login/signup) with password hashing and session support - Replace stub auth with real tRPC calls in login/signup/onboarding pages - Replace mock dashboard data with real API data from hooks - Create service pages: DarkWatch, VoicePrint, SpamShield, HomeTitle, RemoveBrokers, Settings - Update Navbar, TopBar, Sidebar with real user data and correct routes - Add passwordHash field to users schema for credential auth - Fix tests to work with real hooks (mock tRPC/hooks)
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
export default function SocialAuthButtons() {
|
||||
interface SocialAuthButtonsProps {
|
||||
onGoogleSignIn?: () => void;
|
||||
onAppleSignIn?: () => void;
|
||||
}
|
||||
|
||||
export default function SocialAuthButtons(props: SocialAuthButtonsProps) {
|
||||
return (
|
||||
<div class="flex flex-col gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {}}
|
||||
onClick={props.onGoogleSignIn}
|
||||
class="flex items-center justify-center gap-3 w-full px-4 py-2.5 border border-[var(--color-border)] rounded-lg text-sm font-medium text-[var(--color-text-primary)] bg-white hover:bg-[var(--color-bg-secondary)] transition-colors cursor-pointer"
|
||||
>
|
||||
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
|
||||
@@ -16,7 +21,7 @@ export default function SocialAuthButtons() {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {}}
|
||||
onClick={props.onAppleSignIn}
|
||||
class="flex items-center justify-center gap-3 w-full px-4 py-2.5 border border-[var(--color-border)] rounded-lg text-sm font-medium text-white bg-black hover:bg-gray-900 transition-colors cursor-pointer"
|
||||
>
|
||||
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
|
||||
|
||||
@@ -73,12 +73,12 @@ function SettingsIcon() {
|
||||
|
||||
const sidebarLinks: SidebarLink[] = [
|
||||
{ label: "Overview", href: "/dashboard", icon: OverviewIcon },
|
||||
{ label: "DarkWatch", href: "/dashboard/darkwatch", icon: DarkWatchIcon },
|
||||
{ label: "VoicePrint", href: "/dashboard/voiceprint", icon: VoicePrintIcon },
|
||||
{ label: "SpamShield", href: "/dashboard/spamshield", icon: SpamShieldIcon },
|
||||
{ label: "HomeTitle", href: "/dashboard/hometitle", icon: HomeTitleIcon },
|
||||
{ label: "RemoveBrokers", href: "/dashboard/removebrokers", icon: RemoveBrokersIcon },
|
||||
{ label: "Settings", href: "/dashboard/settings", icon: SettingsIcon },
|
||||
{ label: "DarkWatch", href: "/darkwatch", icon: DarkWatchIcon },
|
||||
{ label: "VoicePrint", href: "/voiceprint", icon: VoicePrintIcon },
|
||||
{ label: "SpamShield", href: "/spamshield", icon: SpamShieldIcon },
|
||||
{ label: "HomeTitle", href: "/hometitle", icon: HomeTitleIcon },
|
||||
{ label: "RemoveBrokers", href: "/removebrokers", icon: RemoveBrokersIcon },
|
||||
{ label: "Settings", href: "/settings", icon: SettingsIcon },
|
||||
];
|
||||
|
||||
interface SidebarProps {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createSignal, Show } from "solid-js";
|
||||
import { A } from "@solidjs/router";
|
||||
import { cn } from "~/lib/utils";
|
||||
import { useAuth, useNotifications } from "~/hooks";
|
||||
|
||||
interface TopBarProps {
|
||||
onMenuToggle: () => void;
|
||||
@@ -8,6 +9,18 @@ interface TopBarProps {
|
||||
|
||||
export default function TopBar(props: TopBarProps) {
|
||||
const [showDropdown, setShowDropdown] = createSignal(false);
|
||||
const auth = useAuth();
|
||||
const notifications = useNotifications();
|
||||
|
||||
const initials = () => {
|
||||
const name = auth.user()?.name ?? "";
|
||||
return name
|
||||
.split(" ")
|
||||
.map((n: string) => n[0])
|
||||
.join("")
|
||||
.toUpperCase()
|
||||
.slice(0, 2) || "?";
|
||||
};
|
||||
|
||||
return (
|
||||
<header class="h-16 border-b border-[var(--color-border)] bg-[var(--color-bg)] flex items-center justify-between px-4 lg:px-6">
|
||||
@@ -45,7 +58,9 @@ export default function TopBar(props: TopBarProps) {
|
||||
<path d="M10 2.5a5.5 5.5 0 00-5.5 5.5v3l-1.5 2v1h14v-1l-1.5-2V8a5.5 5.5 0 00-5.5-5.5z" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M8 15.5a2 2 0 004 0" stroke="currentColor" stroke-width="1.5"/>
|
||||
</svg>
|
||||
<span class="absolute top-1.5 right-1.5 w-2 h-2 rounded-full bg-[var(--color-error)]" />
|
||||
<Show when={notifications.unreadCount() > 0}>
|
||||
<span class="absolute top-1.5 right-1.5 w-2 h-2 rounded-full bg-[var(--color-error)]" />
|
||||
</Show>
|
||||
</button>
|
||||
|
||||
<div class="relative">
|
||||
@@ -56,7 +71,7 @@ export default function TopBar(props: TopBarProps) {
|
||||
aria-label="User menu"
|
||||
>
|
||||
<div class="w-8 h-8 rounded-full bg-[var(--color-brand-primary)] flex items-center justify-center text-white text-sm font-medium">
|
||||
JD
|
||||
{initials()}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -68,16 +83,16 @@ export default function TopBar(props: TopBarProps) {
|
||||
/>
|
||||
<div class="absolute right-0 top-full mt-2 z-50 w-48 rounded-xl bg-[var(--color-bg)] border border-[var(--color-border)] shadow-lg py-1">
|
||||
<div class="px-4 py-2 border-b border-[var(--color-border)]">
|
||||
<p class="text-sm font-medium text-[var(--color-text-primary)]">John Doe</p>
|
||||
<p class="text-xs text-[var(--color-text-tertiary)]">john@shieldai.app</p>
|
||||
<p class="text-sm font-medium text-[var(--color-text-primary)]">{auth.user()?.name ?? "User"}</p>
|
||||
<p class="text-xs text-[var(--color-text-tertiary)]">{auth.user()?.email ?? ""}</p>
|
||||
</div>
|
||||
<A href="/dashboard/settings" class="block px-4 py-2 text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)]" onClick={() => setShowDropdown(false)}>
|
||||
<A href="/settings" class="block px-4 py-2 text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)]" onClick={() => setShowDropdown(false)}>
|
||||
Settings
|
||||
</A>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full text-left px-4 py-2 text-sm text-[var(--color-error)] hover:bg-[var(--color-bg-secondary)]"
|
||||
onClick={() => setShowDropdown(false)}
|
||||
onClick={() => { setShowDropdown(false); auth.logout(); }}
|
||||
>
|
||||
Sign out
|
||||
</button>
|
||||
|
||||
@@ -60,17 +60,17 @@ export default function HeroSection(props: HeroSectionProps) {
|
||||
|
||||
<h1 class="text-4xl md:text-6xl lg:text-7xl font-bold tracking-tight mb-6 max-w-4xl">
|
||||
<Typewriter speed={50} delay={400} keepAlive={false}>
|
||||
<span class="text-[var(--color-text-primary)]">AI-Powered </span>
|
||||
<span class="text-text-primary">AI-Powered </span>
|
||||
<span class="text-gradient-primary">Identity Protection</span>
|
||||
<br />
|
||||
<span class="text-[var(--color-text-primary)]">for Everyone</span>
|
||||
<span class="text-text-primary">for Everyone</span>
|
||||
</Typewriter>
|
||||
</h1>
|
||||
|
||||
<p class="text-xl md:text-2xl text-[var(--color-text-secondary)] max-w-2xl mb-10 leading-relaxed">
|
||||
ShieldAI uses advanced AI to monitor, detect, and prevent identity
|
||||
threats in real-time. Your digital identity, protected by
|
||||
intelligence.
|
||||
<p class="text-xl md:text-2xl text-text-secondary max-w-2xl mb-10 leading-relaxed">
|
||||
Threat actors are using AI in multifaceted attacks. ShieldAI evens
|
||||
the playing field using advanced AI to monitor, detect, and prevent
|
||||
identity threats in real-time.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4 mb-8">
|
||||
@@ -86,7 +86,7 @@ export default function HeroSection(props: HeroSectionProps) {
|
||||
</A>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-center gap-x-6 gap-y-2 text-sm text-[var(--color-text-tertiary)]">
|
||||
<div class="flex flex-wrap items-center justify-center gap-x-6 gap-y-2 text-sm text-text-tertiary">
|
||||
<span class="flex items-center gap-1.5">
|
||||
<svg
|
||||
width="16"
|
||||
|
||||
@@ -33,11 +33,9 @@ export default function AppShell(props: AppShellProps) {
|
||||
return (
|
||||
<MetaProvider>
|
||||
<Title>{title()}</Title>
|
||||
<div class="min-h-screen flex flex-col bg-[var(--color-bg)]">
|
||||
<div class="min-h-screen flex flex-col bg-bg">
|
||||
<Navbar />
|
||||
<main class="flex-1 pt-16 bg-dot-grid">
|
||||
{props.children}
|
||||
</main>
|
||||
{props.children}
|
||||
<Footer />
|
||||
</div>
|
||||
</MetaProvider>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cn } from "~/lib/utils";
|
||||
import { Button } from "~/components/ui";
|
||||
import { Typewriter } from "~/components/ui/Typewriter";
|
||||
import { useTheme } from "~/lib/theme";
|
||||
import { useAuth } from "./useAuth";
|
||||
import { SignedIn, SignedOut, UserButton } from "clerk-solidjs";
|
||||
|
||||
function ShieldLogo() {
|
||||
return (
|
||||
@@ -128,7 +128,6 @@ const navLinks = [
|
||||
export default function Navbar() {
|
||||
const [mobileOpen, setMobileOpen] = createSignal(false);
|
||||
const [scrolled, setScrolled] = createSignal(false);
|
||||
const auth = useAuth();
|
||||
|
||||
onMount(() => {
|
||||
const onScroll = () => {
|
||||
@@ -168,23 +167,20 @@ export default function Navbar() {
|
||||
|
||||
<div class="hidden md:flex items-center gap-3">
|
||||
<ThemeToggle />
|
||||
<Show
|
||||
when={auth.isAuthenticated}
|
||||
fallback={
|
||||
<>
|
||||
<Button variant="secondary" size="sm">
|
||||
<A href="/signin">Sign In</A>
|
||||
</Button>
|
||||
<Button variant="primary" size="sm">
|
||||
<A href="/signup">Get Started</A>
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<SignedIn>
|
||||
<UserButton showName />
|
||||
<Button variant="secondary" size="sm">
|
||||
<A href="/dashboard">Dashboard</A>
|
||||
</Button>
|
||||
</Show>
|
||||
</SignedIn>
|
||||
<SignedOut>
|
||||
<Button variant="secondary" size="sm">
|
||||
<A href="/login">Sign In</A>
|
||||
</Button>
|
||||
<Button variant="primary" size="sm">
|
||||
<A href="/signup">Get Started</A>
|
||||
</Button>
|
||||
</SignedOut>
|
||||
</div>
|
||||
|
||||
<div class="flex md:hidden items-center gap-2">
|
||||
@@ -243,23 +239,19 @@ export default function Navbar() {
|
||||
</A>
|
||||
))}
|
||||
<div class="pt-3 flex flex-col gap-2">
|
||||
<Show
|
||||
when={auth.isAuthenticated}
|
||||
fallback={
|
||||
<>
|
||||
<Button variant="secondary" class="w-full">
|
||||
<A href="/signin">Sign In</A>
|
||||
</Button>
|
||||
<Button variant="primary" class="w-full">
|
||||
<A href="/signup">Get Started</A>
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<SignedIn>
|
||||
<Button variant="secondary" class="w-full">
|
||||
<A href="/dashboard">Dashboard</A>
|
||||
</Button>
|
||||
</Show>
|
||||
</SignedIn>
|
||||
<SignedOut>
|
||||
<Button variant="secondary" class="w-full">
|
||||
<A href="/login">Sign In</A>
|
||||
</Button>
|
||||
<Button variant="primary" class="w-full">
|
||||
<A href="/signup">Get Started</A>
|
||||
</Button>
|
||||
</SignedOut>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -79,22 +79,8 @@ describe("PageContainer", () => {
|
||||
});
|
||||
|
||||
describe("useAuth", () => {
|
||||
it("returns isAuthenticated as false by default", async () => {
|
||||
const { useAuth } = await import("./useAuth");
|
||||
const auth = useAuth();
|
||||
expect(auth.isAuthenticated).toBe(false);
|
||||
});
|
||||
|
||||
it("returns null user by default", async () => {
|
||||
const { useAuth } = await import("./useAuth");
|
||||
const auth = useAuth();
|
||||
expect(auth.user).toBe(null);
|
||||
});
|
||||
|
||||
it("provides signIn and signOut methods", async () => {
|
||||
const { useAuth } = await import("./useAuth");
|
||||
const auth = useAuth();
|
||||
expect(typeof auth.signIn).toBe("function");
|
||||
expect(typeof auth.signOut).toBe("function");
|
||||
it("re-exports useAuth from hooks module", async () => {
|
||||
const mod = await import("./useAuth");
|
||||
expect(typeof mod.useAuth).toBe("function");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1 @@
|
||||
import { createSignal } from "solid-js";
|
||||
|
||||
const [getAuth] = (() => {
|
||||
let isAuthenticated = false;
|
||||
let user: { name: string; email: string } | null = null;
|
||||
|
||||
return [
|
||||
() => ({
|
||||
isAuthenticated,
|
||||
user,
|
||||
signIn: () => {},
|
||||
signOut: () => {},
|
||||
}),
|
||||
];
|
||||
})();
|
||||
|
||||
export function useAuth() {
|
||||
return getAuth();
|
||||
}
|
||||
export { useAuth } from "clerk-solidjs";
|
||||
|
||||
Reference in New Issue
Block a user