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
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"boring-avatars": "^2.0.4",
"framer-motion": "^12.38.0",
"marked": "^17.0.5",
"motion": "^12.38.0",
"next": "^14.2.5",
"next-intl": "^4.8.3",
"next-themes": "^0.4.6",
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/app/(authenticated)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ import Breadcrumbs from "@/components/Breadcrumbs";
import LocaleSwitcher from "@/components/LocaleSwitcher";
import PaymentToastListener from "@/components/PaymentToastListener";
import { motion } from "framer-motion";
import AuthGuard from "@/components/AuthGuard";
import { useHydrateMerchantStore } from "@/lib/merchant-store";

export default function AuthenticatedLayout({
children,
}: {
children: React.ReactNode;
}) {
useHydrateMerchantStore();

return (
<div className="flex min-h-screen bg-black">
<AuthGuard>
<div className="flex min-h-screen bg-black">
{/* Sidebar - fixed width for desktop layout offset */}
<Sidebar />
<PaymentToastListener />
Expand Down Expand Up @@ -41,5 +46,6 @@ export default function AuthenticatedLayout({
</div>
</main>
</div>
</AuthGuard>
);
}
3 changes: 3 additions & 0 deletions frontend/src/app/(public)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import HeroSection from "@/components/login/HeroSection";
import LoginForm from "@/components/login/LoginForm";
import Link from "next/link";
import GuestGuard from "@/components/GuestGuard";

export const metadata = {
title: "Login - Stellar Pay",
Expand All @@ -9,6 +10,7 @@ export const metadata = {

export default function LoginPage() {
return (
<GuestGuard>
<main
className="relative min-h-screen flex flex-col text-[#f3f5f7] overflow-x-hidden font-sans bg-[#0b0c10] md:bg-gradient-to-r md:from-[#0b0c10] md:from-50% md:to-[#10131a] md:to-50%"
>
Expand Down Expand Up @@ -46,5 +48,6 @@ export default function LoginPage() {
</div>
</footer>
</main>
</GuestGuard>
);
}
3 changes: 3 additions & 0 deletions frontend/src/app/(public)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import GuestGuard from "@/components/GuestGuard";
import Link from "next/link";
import { motion } from "framer-motion";
import { useState } from "react";
Expand Down Expand Up @@ -541,6 +542,7 @@ function Footer() {

export default function Home() {
return (
<GuestGuard>
<main className="relative min-h-screen overflow-x-hidden">
{/* subtle grid texture */}
<div
Expand All @@ -561,5 +563,6 @@ export default function Home() {
<Footer />
</div>
</main>
</GuestGuard>
);
}
3 changes: 3 additions & 0 deletions frontend/src/app/(public)/register/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import RegistrationForm from "@/components/RegistrationForm";
import Link from "next/link";
import GuestGuard from "@/components/GuestGuard";

export default function RegisterPage() {
return (
<GuestGuard>
<main className="mx-auto flex min-h-screen max-w-lg flex-col justify-center gap-10 px-6 py-16">
<header className="flex flex-col gap-3 text-center">
<p className="font-mono text-xs uppercase tracking-[0.3em] text-mint">Onboarding</p>
Expand All @@ -25,5 +27,6 @@ export default function RegisterPage() {
</p>
</footer>
</main>
</GuestGuard>
);
}
40 changes: 0 additions & 40 deletions frontend/src/app/dashboard/create/page.tsx

This file was deleted.

29 changes: 29 additions & 0 deletions frontend/src/components/AuthGuard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import React, { useEffect, useState } from "react";
import { useRouter, usePathname } from "next/navigation";
import { useMerchantHydrated, useMerchantSession } from "@/lib/merchant-store";

export default function AuthGuard({ children }: { children: React.ReactNode }) {
const hydrated = useMerchantHydrated();
const session = useMerchantSession();
const router = useRouter();
const pathname = usePathname();
const [mounted, setMounted] = useState(false);

useEffect(() => {
setMounted(true);
}, []);

useEffect(() => {
if (mounted && hydrated && !session) {
router.push(`/login?callbackUrl=${encodeURIComponent(pathname)}`);
}
}, [mounted, hydrated, session, router, pathname]);

if (!mounted || !hydrated || !session) {
return null;
}

return <>{children}</>;
}
7 changes: 6 additions & 1 deletion frontend/src/components/CreatePaymentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
useMerchantHydrated,
useMerchantTrustedAddresses,
} from "@/lib/merchant-store";
import { useLocalStorage } from "@/hooks/useLocalStorage";


const API_URL = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:4000";

Expand Down Expand Up @@ -49,6 +49,11 @@ export default function CreatePaymentForm() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [created, setCreated] = useState<CreatedPayment | null>(null);

const [useSessionBranding, setUseSessionBranding] = useState(false);
const [branding, setBranding] = useState(DEFAULT_BRANDING);
const [selectedTrustedAddress, setSelectedTrustedAddress] = useState("");

const apiKey = useMerchantApiKey();
const hydrated = useMerchantHydrated();
const trustedAddresses = useMerchantTrustedAddresses();
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/components/DevTools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ export default function DevTools() {
const parsed = JSON.parse(requestBody);
setRequestBody(JSON.stringify(parsed, null, 2));
setJsonError(null);
} catch (err: any) {
setJsonError(err.message || "Invalid JSON");
} catch (err: unknown) {
setJsonError(err instanceof Error ? err.message : "Invalid JSON");
}
};

Expand All @@ -77,8 +77,8 @@ export default function DevTools() {
JSON.parse(requestBody);
bodyData = requestBody;
}
} catch (err: any) {
throw new Error(`Invalid JSON body: ${err.message}`);
} catch (err: unknown) {
throw new Error(`Invalid JSON body: ${err instanceof Error ? err.message : String(err)}`);
}
}

Expand Down Expand Up @@ -115,9 +115,9 @@ export default function DevTools() {
setResponseTime(Math.round(finishedAt - startedAt));
setResponseBody(formattedBody);

} catch (error: any) {
} catch (error: unknown) {
setResponseStatus(0);
setResponseBody(error.message || "Request failed to send. Check network or CORS.");
setResponseBody(error instanceof Error ? error.message : "Request failed to send. Check network or CORS.");
} finally {
setIsRunning(false);
}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/FirstApiKeyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function FirstApiKeyModal({ isOpen, onClose }: FirstApiKeyModalPr
setApiKey(newKey);
setStoreApiKey(newKey);
toast.success("API Key generated successfully!");
} catch (err: any) {
} catch (err: unknown) {
const message = err instanceof Error ? err.message : "Failed to generate API Key";
toast.error(message);
} finally {
Expand All @@ -40,7 +40,7 @@ export default function FirstApiKeyModal({ isOpen, onClose }: FirstApiKeyModalPr
<div className="flex flex-col gap-2">
<h3 className="text-xl font-bold text-white">Generate your first API key</h3>
<p className="text-slate-400 text-sm">
To start accepting payments, you'll need an API key to authenticate your server-side requests.
To start accepting payments, you&apos;ll need an API key to authenticate your server-side requests.
</p>
</div>

Expand All @@ -57,7 +57,7 @@ export default function FirstApiKeyModal({ isOpen, onClose }: FirstApiKeyModalPr
<div className="flex flex-col gap-2">
<h3 className="text-xl font-bold text-mint">Your API Key is ready!</h3>
<p className="text-slate-400 text-sm">
Copy this key and save it somewhere secure. You won't be able to see it again after closing this window.
Copy this key and save it somewhere secure. You won&apos;t be able to see it again after closing this window.
</p>
</div>

Expand Down
30 changes: 30 additions & 0 deletions frontend/src/components/GuestGuard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import React, { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { useMerchantHydrated, useMerchantSession } from "@/lib/merchant-store";

export default function GuestGuard({ children }: { children: React.ReactNode }) {
const hydrated = useMerchantHydrated();
const session = useMerchantSession();
const router = useRouter();
const [mounted, setMounted] = useState(false);

useEffect(() => {
setMounted(true);
}, []);

useEffect(() => {
if (mounted && hydrated && session) {
router.push("/dashboard");
}
}, [mounted, hydrated, session, router]);

// If we are definitely authenticated, hide the public UI so it doesn't flash
// before the redirect kicks in.
if (mounted && hydrated && session) {
return null;
}

return <>{children}</>;
}
Loading
Loading