-
+
- P2P
+ Vantage P2P
diff --git a/public/vantage.svg b/public/vantage.svg
new file mode 100644
index 0000000..d08ab51
--- /dev/null
+++ b/public/vantage.svg
@@ -0,0 +1 @@
+
diff --git a/src/api/viewService.ts b/src/api/viewService.ts
index 3101153..98aeede 100644
--- a/src/api/viewService.ts
+++ b/src/api/viewService.ts
@@ -1,6 +1,6 @@
import { APP_ID, BASE_URL } from "../config";
-const TOKEN_KEY = "p2p_token";
+const TOKEN_KEY = "vantage_p2p_token";
// Error carrying the HTTP status so callers can distinguish e.g. a 403
// (authenticated but not entitled to this app) from a generic failure.
@@ -29,7 +29,7 @@ function headers(): Record {
async function parseResponse(res: Response): Promise {
if (res.status === 401) {
localStorage.removeItem(TOKEN_KEY);
- localStorage.removeItem("p2p_user");
+ localStorage.removeItem("vantage_p2p_user");
window.location.href = `${import.meta.env.BASE_URL}login`;
throw new ApiError(401, "Unauthorized");
}
diff --git a/src/components/Brand.tsx b/src/components/Brand.tsx
new file mode 100644
index 0000000..8d99ebe
--- /dev/null
+++ b/src/components/Brand.tsx
@@ -0,0 +1,23 @@
+import { cn } from "@/lib/utils";
+
+interface BrandProps {
+ /** Show only "V" (collapsed sidebar rail). */
+ markOnly?: boolean;
+ /** Paler gradient for dark backgrounds. */
+ light?: boolean;
+ className?: string;
+}
+
+/** VANTAGE wordmark — bold, wide-tracked, brand-gradient text. */
+export function Brand({ markOnly = false, light = false, className }: BrandProps) {
+ return (
+
+ {markOnly ? "V" : "VANTAGE"}
+
+ );
+}
diff --git a/src/components/DynamicHeader.tsx b/src/components/DynamicHeader.tsx
index ae09c8d..bd79de8 100644
--- a/src/components/DynamicHeader.tsx
+++ b/src/components/DynamicHeader.tsx
@@ -13,6 +13,7 @@ import {
Sun,
} from "lucide-react";
import { cn } from "@/lib/utils";
+import { Brand } from "@/components/Brand";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
@@ -149,13 +150,7 @@ export default function DynamicHeader({
{/* On desktop the sidebar carries the identity; repeating the logo
here would double it up. */}
- {sidebar.isMobile && (
-
- )}
+ {sidebar.isMobile && }
@@ -267,13 +262,8 @@ function SidebarBody({
railed && "px-0",
)}
>
- {/* 43×20 wordmark — fits the 56px rail as-is, so it needs no separate
- collapsed mark. Inverted in dark mode like the top-bar copy. */}
-
+ {/* Full wordmark is too wide for the 56px rail, so collapse to the mark. */}
+
{!railed && (
Procure to Pay
diff --git a/src/components/FormModal.tsx b/src/components/FormModal.tsx
index ef5a091..020972a 100644
--- a/src/components/FormModal.tsx
+++ b/src/components/FormModal.tsx
@@ -218,7 +218,7 @@ export default function FormModal({ activityId, instanceId, title, onClose, onSu
{/* Radix supplies the focus trap, Escape handling, aria-modal, scroll lock
and focus restore that the hand-rolled overlay never had. */}
submitting && e.preventDefault()}
>
@@ -228,7 +228,7 @@ export default function FormModal({ activityId, instanceId, title, onClose, onSu
-
+
{isPending ? (
{Array.from({ length: 4 }).map((_, r) => (
diff --git a/src/hooks/ThemeContext.tsx b/src/hooks/ThemeContext.tsx
index c690d1f..ebc30c5 100644
--- a/src/hooks/ThemeContext.tsx
+++ b/src/hooks/ThemeContext.tsx
@@ -14,7 +14,7 @@ const ThemeContext = createContext({
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState(() => {
- const stored = localStorage.getItem("p2p_theme");
+ const stored = localStorage.getItem("vantage_p2p_theme");
if (stored === "dark" || stored === "light") return stored;
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
});
@@ -22,7 +22,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
const root = document.documentElement;
root.classList.toggle("dark", theme === "dark");
- localStorage.setItem("p2p_theme", theme);
+ localStorage.setItem("vantage_p2p_theme", theme);
}, [theme]);
// The colour-transition is opt-in per switch rather than a standing global
diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts
index 56b5d55..bffb752 100644
--- a/src/hooks/useAuth.ts
+++ b/src/hooks/useAuth.ts
@@ -2,8 +2,8 @@ import { useState, useCallback } from "react";
import type { User } from "../zino-sdk/types";
import { BASE_URL } from "../config";
-const TOKEN_KEY = "p2p_token";
-const USER_KEY = "p2p_user";
+const TOKEN_KEY = "vantage_p2p_token";
+const USER_KEY = "vantage_p2p_user";
interface LoginParams {
email: string;
diff --git a/src/hooks/useSidebar.ts b/src/hooks/useSidebar.ts
index e5c788e..3aff72b 100644
--- a/src/hooks/useSidebar.ts
+++ b/src/hooks/useSidebar.ts
@@ -7,7 +7,7 @@
import { useCallback, useEffect, useState } from "react";
-const KEY = "p2p_sidebar_open";
+const KEY = "vantage_p2p_sidebar_open";
/** Below this the sidebar becomes an overlay drawer rather than a rail. */
export const MOBILE_BREAKPOINT = 1024;
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx
index e1f5d09..34ff003 100644
--- a/src/pages/Login.tsx
+++ b/src/pages/Login.tsx
@@ -1,6 +1,7 @@
import { useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { ArrowRight, BarChart3, Bot, Loader2, ShieldCheck, Zap } from "lucide-react";
+import { Brand } from "@/components/Brand";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -75,11 +76,7 @@ export default function Login() {
/>
-
+
@@ -117,11 +114,7 @@ export default function Login() {
{/* ── Right — form ──────────────────────────────────────────────────── */}
-
+
Sign in
diff --git a/src/zino-sdk/client.ts b/src/zino-sdk/client.ts
index a01b6c0..fc621b1 100644
--- a/src/zino-sdk/client.ts
+++ b/src/zino-sdk/client.ts
@@ -1,6 +1,6 @@
import type { ApiError, ZinoClientConfig } from "./types";
-const TOKEN_KEY = "p2p_token";
+const TOKEN_KEY = "vantage_p2p_token";
/**
* Core HTTP client for the Zino API.