feat: vantage_ prefix on localStorage keys; VANTAGE wordmark + favicon; fix form dialog scroll with file preview
This commit is contained in:
parent
9b1c3fbc32
commit
591c2ac5d5
@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" type="image/svg+xml" href="./zino.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="./vantage.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<title>P2P</title>
|
||||
<title>Vantage P2P</title>
|
||||
<!--BASE_HREF-->
|
||||
<script src="./config.js"></script>
|
||||
</head>
|
||||
|
||||
1
public/vantage.svg
Normal file
1
public/vantage.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="7" fill="#7c3aed"/><path d="M8 9h4.4l3.6 7.5L19.6 9H24l-8 15z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 176 B |
@ -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<string, string> {
|
||||
async function parseResponse<T>(res: Response): Promise<T> {
|
||||
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");
|
||||
}
|
||||
|
||||
23
src/components/Brand.tsx
Normal file
23
src/components/Brand.tsx
Normal file
@ -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 (
|
||||
<span
|
||||
className={cn("gradient-text text-base font-bold tracking-[0.25em]", className)}
|
||||
style={
|
||||
light ? { backgroundImage: "linear-gradient(135deg, #c4b5fd, #f0abfc)" } : undefined
|
||||
}
|
||||
>
|
||||
{markOnly ? "V" : "VANTAGE"}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -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 && (
|
||||
<img
|
||||
src={import.meta.env.BASE_URL + "zino.svg"}
|
||||
alt="Zino"
|
||||
className="h-5 w-auto dark:brightness-0 dark:invert"
|
||||
/>
|
||||
)}
|
||||
{sidebar.isMobile && <Brand />}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
@ -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. */}
|
||||
<img
|
||||
src={import.meta.env.BASE_URL + "zino.svg"}
|
||||
alt="Zino"
|
||||
className="h-5 w-auto shrink-0 dark:brightness-0 dark:invert"
|
||||
/>
|
||||
{/* Full wordmark is too wide for the 56px rail, so collapse to the mark. */}
|
||||
<Brand markOnly={railed} />
|
||||
{!railed && (
|
||||
<span className="truncate text-2xs font-medium tracking-wide text-muted-foreground">
|
||||
Procure to Pay
|
||||
|
||||
@ -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. */}
|
||||
<DialogContent
|
||||
className="max-h-[90vh] gap-0 overflow-hidden p-0 sm:max-w-xl"
|
||||
className="flex max-h-[90vh] flex-col gap-0 overflow-hidden p-0 sm:max-w-xl"
|
||||
onInteractOutside={(e) => submitting && e.preventDefault()}
|
||||
>
|
||||
<DialogHeader className="border-b border-border px-5 py-4 text-left">
|
||||
@ -228,7 +228,7 @@ export default function FormModal({ activityId, instanceId, title, onClose, onSu
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="scrollbar-thin flex-1 overflow-y-auto px-5 py-4">
|
||||
<div className="scrollbar-thin min-h-0 flex-1 overflow-y-auto px-5 py-4">
|
||||
{isPending ? (
|
||||
<div className="space-y-4">
|
||||
{Array.from({ length: 4 }).map((_, r) => (
|
||||
|
||||
@ -14,7 +14,7 @@ const ThemeContext = createContext<ThemeContextValue>({
|
||||
|
||||
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||
const [theme, setTheme] = useState<Theme>(() => {
|
||||
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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<img
|
||||
src={import.meta.env.BASE_URL + "zino.svg"}
|
||||
alt="Zino"
|
||||
className="absolute left-16 top-10 z-10 h-5 w-auto brightness-0 invert"
|
||||
/>
|
||||
<Brand light className="absolute left-16 top-10 z-10" />
|
||||
|
||||
<div className="relative z-10 max-w-lg">
|
||||
<span className="mb-8 inline-flex items-center gap-2 rounded-full border border-violet-300/30 bg-primary/20 px-3 py-1.5 text-sm font-medium text-violet-200">
|
||||
@ -117,11 +114,7 @@ export default function Login() {
|
||||
{/* ── Right — form ──────────────────────────────────────────────────── */}
|
||||
<div className="flex flex-1 items-center justify-center px-6">
|
||||
<div className="w-full max-w-[340px]">
|
||||
<img
|
||||
src={import.meta.env.BASE_URL + "zino.svg"}
|
||||
alt="Zino"
|
||||
className="mb-10 h-5 w-auto dark:invert lg:hidden"
|
||||
/>
|
||||
<Brand className="mb-10 lg:hidden" />
|
||||
|
||||
<div className="mb-8">
|
||||
<h2 className="text-4xl font-bold tracking-tight text-foreground">Sign in</h2>
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user