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
11 changes: 10 additions & 1 deletion frontend/app/dashboard/invoices/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button';
import { ArrowLeft, Download } from 'lucide-react';
import Link from 'next/link';
import { Skeleton } from '@/components/ui/skeleton';
import { PageBreadcrumb } from '@/components/layout/PageBreadcrumb';

export default function InvoiceDetailPage() {
const params = useParams();
Expand Down Expand Up @@ -51,7 +52,15 @@ export default function InvoiceDetailPage() {
};

return (
<div className="invoice-print-page space-y-6">
<div className="space-y-6 invoice-print-page">
<PageBreadcrumb
items={[
{ label: 'Dashboard', href: '/dashboard' },
{ label: 'Invoices', href: '/dashboard/invoices' },
]}
currentPage={`Invoice ${rawId}`}
/>

<Link href="/dashboard/invoices" className="no-print inline-flex">
<Button variant="ghost" className="mb-4">
<ArrowLeft className="mr-2 h-4 w-4" />
Expand Down
9 changes: 9 additions & 0 deletions frontend/app/dashboard/projects/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useAgenticPay } from '@/lib/hooks/useAgenticPay';
import { useAccount } from 'wagmi';
import { toast } from 'sonner';
import { api } from '@/lib/api';
import { PageBreadcrumb } from '@/components/layout/PageBreadcrumb';

export default function ProjectDetailPage() {
const params = useParams();
Expand Down Expand Up @@ -91,6 +92,14 @@ export default function ProjectDetailPage() {

return (
<div className="space-y-6">
<PageBreadcrumb
items={[
{ label: 'Dashboard', href: '/dashboard' },
{ label: 'Projects', href: '/dashboard/projects' },
]}
currentPage={project.title}
/>

<Link href="/dashboard/projects">
<Button variant="ghost" className="mb-4">
<ArrowLeft className="h-4 w-4 mr-2" />
Expand Down
100 changes: 39 additions & 61 deletions frontend/components/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { useState } from 'react';
import { useAuthStore } from '@/store/useAuthStore';
import { useRouter, usePathname } from 'next/navigation';
import { useThemeStore } from '@/store/useThemeStore';
import { useRouter } from 'next/navigation';
import { Button } from '@/components/ui/button';
Expand All @@ -16,13 +17,19 @@
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Bell, LogOut, User, Settings, Sun, Moon, Clock } from 'lucide-react';
import { toast } from 'sonner';
<<<<<<< feat/qr-code

=======
>>>>>>> main
import { useEffect, useState } from 'react';
// 1. I added useNetwork to the existing wagmi import
import { useDisconnect, useAccount } from 'wagmi';
import { web3auth } from '@/lib/web3auth';
import {
Breadcrumb,
BreadcrumbList,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbPage,

Check warning on line 29 in frontend/components/layout/Header.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 20)

'BreadcrumbPage' is defined but never used

Check warning on line 29 in frontend/components/layout/Header.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 22)

'BreadcrumbPage' is defined but never used
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import { getDashboardBreadcrumbs } from '@/lib/breadcrumbs';
import { ThemeSettingsModal } from '@/components/theme/ThemeSettingsModal';

// 2. I built the isolated NetworkIndicator component right here
Expand Down Expand Up @@ -93,9 +100,16 @@

export function Header() {
const { name, email, address, logout } = useAuthStore();
const { isDark, mode, toggle, setIsDark } = useThemeStore();

Check warning on line 103 in frontend/components/layout/Header.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 20)

'toggle' is assigned a value but never used

Check warning on line 103 in frontend/components/layout/Header.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 22)

'toggle' is assigned a value but never used
const { disconnect } = useDisconnect();
const router = useRouter();
const pathname = usePathname();
const [breadcrumbs, setBreadcrumbs] = useState<any[]>([]);

Check warning on line 107 in frontend/components/layout/Header.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 20)

Unexpected any. Specify a different type

Check warning on line 107 in frontend/components/layout/Header.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 22)

Unexpected any. Specify a different type

useEffect(() => {
const items = getDashboardBreadcrumbs(pathname);
setBreadcrumbs(items);
}, [pathname]);
const [themeSettingsOpen, setThemeSettingsOpen] = useState(false);

const handleLogout = async () => {
Expand Down Expand Up @@ -175,58 +189,6 @@
)}
</Button>

<<<<<<< feat/qr-code
<div className="flex items-center gap-4">

{/* 3. I dropped the new component right here! */}
<NetworkIndicator />

{/* Notifications */}
<Button variant="ghost" size="icon" className="relative">
<Bell className="h-5 w-5" />
<span className="absolute top-1 right-1 w-2 h-2 bg-red-500 rounded-full" />
</Button>

{/* User menu */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="flex items-center gap-3 h-auto py-2 px-3">
<Avatar className="h-8 w-8">
<AvatarFallback className="bg-gradient-to-r from-blue-500 to-purple-500 text-white">
{initials}
</AvatarFallback>
</Avatar>
<div className="hidden sm:block text-left">
<p className="text-sm font-medium text-gray-900">{name || 'User'}</p>
<p className="text-xs text-gray-500">{shortAddress}</p>
</div>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
<DropdownMenuLabel>
<div className="flex flex-col space-y-1">
<p className="text-sm font-medium">{name || 'User'}</p>
<p className="text-xs text-gray-500">{email || 'No email'}</p>
<p className="text-xs text-gray-400 font-mono">{shortAddress}</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<User className="mr-2 h-4 w-4" />
Profile
</DropdownMenuItem>
<DropdownMenuItem>
<Settings className="mr-2 h-4 w-4" />
Settings
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleLogout} className="text-red-600">
<LogOut className="mr-2 h-4 w-4" />
Logout
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
=======
{/* Theme schedule settings */}
<Button
variant="ghost"
Expand Down Expand Up @@ -279,15 +241,31 @@
</DropdownMenuContent>
</DropdownMenu>
</div>
>>>>>>> main
</div>
</div>

{/* Breadcrumb Navigation */}
{breadcrumbs.length > 0 && (
<div className="border-t border-gray-100 bg-gray-50/50 px-4 sm:px-6 py-3">
<Breadcrumb>
<BreadcrumbList>
{breadcrumbs.map((item, index) => (
<div key={index} className="flex items-center gap-1.5">
<BreadcrumbItem>
<BreadcrumbLink href={item.href}>
{item.label}
</BreadcrumbLink>
</BreadcrumbItem>
{index < breadcrumbs.length - 1 && <BreadcrumbSeparator />}
</div>
))}
</BreadcrumbList>
</Breadcrumb>
</div>
)}
</header>

<ThemeSettingsModal open={themeSettingsOpen} onClose={() => setThemeSettingsOpen(false)} />
</>
);
<<<<<<< feat/qr-code
}
=======
}
>>>>>>> main
43 changes: 43 additions & 0 deletions frontend/components/layout/PageBreadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use client';

import React from 'react';
import {
Breadcrumb,
BreadcrumbList,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';

export interface BreadcrumbItemData {
label: string;
href: string;
}

interface PageBreadcrumbProps {
items: BreadcrumbItemData[];
currentPage: string;
}

export function PageBreadcrumb({ items, currentPage }: PageBreadcrumbProps) {
return (
<Breadcrumb className="mb-6">
<BreadcrumbList>
{items.map((item, index) => (
<React.Fragment key={index}>
<BreadcrumbItem>
<BreadcrumbLink href={item.href}>
{item.label}
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
</React.Fragment>
))}
<BreadcrumbItem>
<BreadcrumbPage>{currentPage}</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
);
}
130 changes: 130 additions & 0 deletions frontend/components/ui/breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { ChevronRight } from "lucide-react"
import { cn } from "@/lib/utils"
import Link from "next/link"

const breadcrumbVariants = cva("flex items-center gap-1.5")

interface BreadcrumbProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof breadcrumbVariants> {}

const Breadcrumb = React.forwardRef<HTMLDivElement, BreadcrumbProps>(
({ className, ...props }, ref) => (
<nav
ref={ref}
aria-label="breadcrumb"
className={cn(breadcrumbVariants(), className)}
{...props}
/>
)
)
Breadcrumb.displayName = "Breadcrumb"

interface BreadcrumbListProps extends React.HTMLAttributes<HTMLOListElement> {}

Check failure on line 26 in frontend/components/ui/breadcrumb.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 20)

An interface declaring no members is equivalent to its supertype

Check failure on line 26 in frontend/components/ui/breadcrumb.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 22)

An interface declaring no members is equivalent to its supertype

const BreadcrumbList = React.forwardRef<HTMLOListElement, BreadcrumbListProps>(
({ className, ...props }, ref) => (
<ol
ref={ref}
className={cn("flex flex-wrap items-center gap-1.5", className)}
{...props}
/>
)
)
BreadcrumbList.displayName = "BreadcrumbList"

interface BreadcrumbItemProps extends React.HTMLAttributes<HTMLLIElement> {}

Check failure on line 39 in frontend/components/ui/breadcrumb.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 20)

An interface declaring no members is equivalent to its supertype

Check failure on line 39 in frontend/components/ui/breadcrumb.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 22)

An interface declaring no members is equivalent to its supertype

const BreadcrumbItem = React.forwardRef<HTMLLIElement, BreadcrumbItemProps>(
({ className, ...props }, ref) => (
<li
ref={ref}
className={cn("inline-flex items-center gap-1.5", className)}
{...props}
/>
)
)
BreadcrumbItem.displayName = "BreadcrumbItem"

interface BreadcrumbLinkProps
extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
asChild?: boolean
href: string
}

const BreadcrumbLink = React.forwardRef<HTMLAnchorElement, BreadcrumbLinkProps>(
({ className, href, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : Link

return (
<Comp
ref={ref}
href={href}
className={cn(
"text-sm font-medium text-gray-700 hover:text-gray-900 transition-colors cursor-pointer",
className
)}
{...props}
/>
)
}
)
BreadcrumbLink.displayName = "BreadcrumbLink"

interface BreadcrumbPageProps

Check failure on line 77 in frontend/components/ui/breadcrumb.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 20)

An interface declaring no members is equivalent to its supertype

Check failure on line 77 in frontend/components/ui/breadcrumb.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node.js 22)

An interface declaring no members is equivalent to its supertype
extends React.HTMLAttributes<HTMLSpanElement> {}

const BreadcrumbPage = React.forwardRef<HTMLSpanElement, BreadcrumbPageProps>(
({ className, ...props }, ref) => (
<span
ref={ref}
role="doc-biblioentry"
aria-current="page"
className={cn(
"text-sm font-medium text-gray-900",
className
)}
{...props}
/>
)
)
BreadcrumbPage.displayName = "BreadcrumbPage"

interface BreadcrumbSeparatorProps
extends React.HTMLAttributes<HTMLDivElement> {
icon?: React.ReactNode
}

const BreadcrumbSeparator = ({ className, icon, ...props }: BreadcrumbSeparatorProps) =>
icon ? (
<div
role="presentation"
aria-hidden="true"
className={cn("text-gray-400", className)}
{...props}
>
{icon}
</div>
) : (
<div
role="presentation"
aria-hidden="true"
className={cn("text-gray-400", className)}
{...props}
>
<ChevronRight className="h-4 w-4" />
</div>
)
BreadcrumbSeparator.displayName = "BreadcrumbSeparator"

export {
Breadcrumb,
BreadcrumbList,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbPage,
BreadcrumbSeparator,
}
Loading
Loading