refactor(ui): move every page header onto PageHeader and drop the legacy one

Virtual Keys, Budgets, Projects, Access Groups, Guardrails Monitor and Cost
Optimization all move onto the shared PageHeader, matching the Teams page.
That empties LegacyPageHeader, so it and its test are deleted.

Each page now uses its own sidebar icon, so the nav and the page agree:
Virtual Keys keeps KeyRound and Budgets keeps Wallet, Projects picks up
Folder and Access Groups picks up Boxes, and Guardrails Monitor swaps the
indigo Shield for the sidebar's HeartPulse. Cost Optimization keeps
PiggyBank but drops its hardcoded size and stroke, which PageHeader owns.

Control rows follow the spec instead of each page inventing one. Virtual
Keys had its create button rendered as a sibling below the header, Budgets
hand-rolled a row with a bottom border that closed the header off, and
Projects and Access Groups sat their button next to the title. All four now
pass primaryAction. Guardrails Monitor's date picker moves out of the parent
and joins Export Data in utilities. Cost Optimization's tabs move into the
tabs slot with the standard 22px spacing.

Page insets go to p-8 with a 24px gap to content, replacing p-6 px-12,
p-6, mx-4 and py-2.

Every page test now asserts its heading, subtext and sidebar icon. Swapping
any of the six icons fails its suite.
This commit is contained in:
Yuneng Jiang 2026-08-25 23:19:22 -07:00
parent 3e2927de9a
commit ca77b32454
No known key found for this signature in database
16 changed files with 198 additions and 226 deletions

View file

@ -99,6 +99,8 @@ describe("AccessGroupsPage", () => {
renderWithProviders(<AccessGroupsPage />);
expect(screen.getByRole("heading", { name: "Access Groups" })).toBeInTheDocument();
expect(screen.getByText("Manage resource permissions for your organization")).toBeInTheDocument();
expect(document.querySelector(".lucide-boxes")).not.toBeNull();
expect(screen.getByText("Manage resource permissions for your organization")).toBeInTheDocument();
});
it("shows the Create Access Group button for an admin", () => {

View file

@ -1,9 +1,9 @@
import { AccessGroupResponse, useAccessGroups } from "@/app/(dashboard)/hooks/accessGroups/useAccessGroups";
import { useDeleteAccessGroup } from "@/app/(dashboard)/hooks/accessGroups/useDeleteAccessGroup";
import { Plus, SearchIcon, X } from "lucide-react";
import { Boxes, Plus, SearchIcon, X } from "lucide-react";
import { useMemo, useState } from "react";
import DeleteResourceModal from "@/components/common_components/DeleteResourceModal";
import { LegacyPageHeader } from "@/components/shared/LegacyPageHeader";
import { PageHeader } from "@/components/shared/PageHeader";
import { Button } from "@/components/ui/button";
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@/components/ui/input-group";
import { AccessGroupDetail } from "./AccessGroupsDetailsPage";
@ -59,23 +59,22 @@ export function AccessGroupsPage() {
}
return (
<div className="p-6 px-12">
<div className="mb-4">
<LegacyPageHeader
title="Access Groups"
subtitle="Manage resource permissions for your organization"
actions={
canModify ? (
<Button onClick={() => setIsCreateModalVisible(true)}>
<Plus className="size-4" />
Create Access Group
</Button>
) : undefined
}
/>
</div>
<div className="p-8">
<PageHeader
icon={<Boxes />}
title="Access Groups"
subtitle="Manage resource permissions for your organization"
primaryAction={
canModify ? (
<Button onClick={() => setIsCreateModalVisible(true)}>
<Plus className="size-4" />
Create Access Group
</Button>
) : undefined
}
/>
<div className="mb-3 flex items-center">
<div className="mt-6 mb-3 flex items-center">
<InputGroup className="max-w-[400px]">
<InputGroupAddon>
<SearchIcon className="size-4 text-muted-foreground" />

View file

@ -85,6 +85,14 @@ describe("Budget Panel", () => {
respondWith(DEFAULT_ROWS, 1);
});
it("renders the standard page header with the sidebar's Budgets icon", async () => {
const { container } = renderPanel();
expect(await screen.findByRole("heading", { level: 1, name: "Budgets" })).toBeInTheDocument();
expect(screen.getByText("Spend, TPM and RPM limits you can assign to customers.")).toBeInTheDocument();
expect(container.querySelector(".lucide-wallet")).not.toBeNull();
});
it("loads the first page of budgets, newest first", async () => {
renderPanel();
await waitFor(() => expect(getMock).toHaveBeenCalled());

View file

@ -9,8 +9,7 @@ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { prism } from "react-syntax-highlighter/dist/esm/styles/prism";
import { useSyntaxTheme } from "@/hooks/useSyntaxTheme";
import { LegacyPageHeader } from "@/components/shared/LegacyPageHeader";
import { ToolbarSeparator } from "@/components/shared/ToolbarSeparator";
import { PageHeader } from "@/components/shared/PageHeader";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import DeleteResourceModal from "@/components/common_components/DeleteResourceModal";
@ -79,34 +78,37 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
};
return (
<div className="flex h-full flex-col gap-4 p-6 px-12">
<LegacyPageHeader
icon={<Wallet className="size-5" />}
title="Budgets"
subtitle="Spend, TPM and RPM limits you can assign to customers."
/>
<Tabs defaultValue="budgets" className="min-h-0 flex-1 gap-0">
<div className="flex items-center gap-4 border-b border-border">
{canModify && (
<>
<main className="flex h-full flex-col p-8">
<Tabs defaultValue="budgets" className="min-h-0 flex-1 gap-6">
<PageHeader
icon={<Wallet />}
title="Budgets"
subtitle="Spend, TPM and RPM limits you can assign to customers."
primaryAction={
canModify ? (
<Button onClick={() => setIsCreateModelVisible(true)}>
<Plus className="size-4" />
Create Budget
</Button>
<ToolbarSeparator className="h-6" />
</>
) : undefined
}
tabs={({ leadingControls }) => (
<TabsList
variant="line"
className="gap-0 p-0 [&>[data-slot=tabs-trigger]+[data-slot=tabs-trigger]]:ml-[22px]"
>
{leadingControls}
<TabsTrigger value="budgets" className="flex-none px-0 py-[7px] data-active:font-semibold">
Budgets
</TabsTrigger>
<TabsTrigger value="examples" className="flex-none px-0 py-[7px] data-active:font-semibold">
Examples
</TabsTrigger>
</TabsList>
)}
<TabsList variant="line">
<TabsTrigger value="budgets" className="flex-none px-4">
Budgets
</TabsTrigger>
<TabsTrigger value="examples" className="flex-none px-4">
Examples
</TabsTrigger>
</TabsList>
</div>
/>
<TabsContent value="budgets" className="flex min-h-0 flex-1 flex-col" keepMounted>
<div className="flex min-h-0 flex-1 flex-col pt-6">
<div className="flex min-h-0 flex-1 flex-col">
<BudgetModal isModalVisible={isCreateModelVisible} setIsModalVisible={setIsCreateModelVisible} />
{selectedBudget && (
<EditBudgetModal
@ -172,7 +174,7 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
</div>
</TabsContent>
</Tabs>
</div>
</main>
);
};

View file

@ -44,6 +44,14 @@ describe("CostOptimizationView", () => {
useAuthorizedMock.mockReturnValue({ accessToken: "test-token", userId: "u1", userRole: "Admin" });
});
it("renders the standard page header with the sidebar's Cost Optimization icon", () => {
const { container, getByRole, getByText } = renderView();
expect(getByRole("heading", { level: 1, name: "Cost Optimization" })).toBeInTheDocument();
expect(getByText(/Track and configure the mechanisms that save you money/)).toBeInTheDocument();
expect(container.querySelector(".lucide-piggy-bank")).not.toBeNull();
});
it("renders the four cost-optimization tabs", () => {
const { getByText } = renderView();

View file

@ -6,6 +6,7 @@ import { Info, PiggyBank } from "lucide-react";
import useCan from "@/app/(dashboard)/hooks/useCan";
import PaginationStatusAlerts from "@/components/shared/PaginationStatusAlerts";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { PageHeader } from "@/components/shared/PageHeader";
import UsageTab from "./UsageTab";
import PromptCompressionTab from "./PromptCompressionTab";
import PromptCachingTab from "./PromptCachingTab";
@ -32,63 +33,63 @@ const CostOptimizationView: React.FC<CostOptimizationViewProps> = ({ accessToken
};
return (
<div className="w-full space-y-6 p-6">
<div>
<div className="flex items-center gap-2">
<PiggyBank className="size-6 text-primary" strokeWidth={1.75} />
<h1 className="text-xl font-semibold text-foreground">Cost Optimization</h1>
</div>
<p className="mt-1 text-sm text-muted-foreground">
Track and configure the mechanisms that save you money: prompt compression and prompt caching. Auto routers
live under Models + Endpoints, on the Auto-Routers tab
</p>
</div>
<div
role="alert"
className="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 rounded-lg border border-border bg-muted/50 px-4 py-4"
>
<Info className="mt-0.5 size-5 text-primary" aria-hidden="true" />
<p className="font-medium text-foreground">This is an experimental dashboard</p>
<p className="col-start-2 text-sm text-muted-foreground">
Have feedback? Join the discussion{" "}
<a
href="https://github.com/BerriAI/litellm/discussions/32168"
target="_blank"
rel="noopener noreferrer"
className="text-primary underline underline-offset-2"
>
here
</a>
</p>
</div>
<PaginationStatusAlerts
isFetchingMore={activity.isFetchingMore}
cancelled={activity.cancelled}
progress={activity.progress}
cancel={activity.cancel}
/>
<Tabs defaultValue="usage" onValueChange={handleTabChange}>
<TabsList variant="line" className="h-auto w-full justify-start rounded-none p-0">
<TabsTrigger value="usage" className="flex-none rounded-none px-4 py-2">
Overall
</TabsTrigger>
{canViewProxyWideCostData && (
<>
<TabsTrigger value="compression" className="flex-none rounded-none px-4 py-2">
Prompt Compression
<main className="w-full p-8">
<Tabs defaultValue="usage" onValueChange={handleTabChange} className="gap-6">
<PageHeader
icon={<PiggyBank />}
title="Cost Optimization"
subtitle="Track and configure the mechanisms that save you money: prompt compression and prompt caching. Auto routers live under Models + Endpoints, on the Auto-Routers tab"
tabs={({ leadingControls }) => (
<TabsList
variant="line"
className="gap-0 p-0 [&>[data-slot=tabs-trigger]+[data-slot=tabs-trigger]]:ml-[22px]"
>
{leadingControls}
<TabsTrigger value="usage" className="flex-none px-0 py-[7px] data-active:font-semibold">
Overall
</TabsTrigger>
<TabsTrigger value="caching" className="flex-none rounded-none px-4 py-2">
Prompt Caching
</TabsTrigger>
<TabsTrigger value="autorouter-usage" className="flex-none rounded-none px-4 py-2">
Auto-Router
</TabsTrigger>
</>
{canViewProxyWideCostData && (
<>
<TabsTrigger value="compression" className="flex-none px-0 py-[7px] data-active:font-semibold">
Prompt Compression
</TabsTrigger>
<TabsTrigger value="caching" className="flex-none px-0 py-[7px] data-active:font-semibold">
Prompt Caching
</TabsTrigger>
<TabsTrigger value="autorouter-usage" className="flex-none px-0 py-[7px] data-active:font-semibold">
Auto-Router
</TabsTrigger>
</>
)}
</TabsList>
)}
</TabsList>
/>
<div
role="alert"
className="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 rounded-lg border border-border bg-muted/50 px-4 py-4"
>
<Info className="mt-0.5 size-5 text-primary" aria-hidden="true" />
<p className="font-medium text-foreground">This is an experimental dashboard</p>
<p className="col-start-2 text-sm text-muted-foreground">
Have feedback? Join the discussion{" "}
<a
href="https://github.com/BerriAI/litellm/discussions/32168"
target="_blank"
rel="noopener noreferrer"
className="text-primary underline underline-offset-2"
>
here
</a>
</p>
</div>
<PaginationStatusAlerts
isFetchingMore={activity.isFetchingMore}
cancelled={activity.cancelled}
progress={activity.progress}
cancel={activity.cancel}
/>
<TabsContent value="usage" keepMounted={visitedTabs.includes("usage")}>
<UsageTab accessToken={accessToken} activity={activity} />
</TabsContent>
@ -106,7 +107,7 @@ const CostOptimizationView: React.FC<CostOptimizationViewProps> = ({ accessToken
</>
)}
</Tabs>
</div>
</main>
);
};

View file

@ -41,27 +41,32 @@ export default function GuardrailsMonitorView({ accessToken = null }: Guardrails
setView({ type: "overview" });
};
const dateRangeControl = (
<AdvancedDatePicker value={dateValue} onValueChange={handleDateChange} label="" showTimeRange={false} />
);
return (
<div className="p-6 w-full min-w-0 flex-1">
<div className="flex items-center justify-end mb-4">
<AdvancedDatePicker value={dateValue} onValueChange={handleDateChange} label="" showTimeRange={false} />
</div>
<main className="w-full min-w-0 flex-1 p-8">
{view.type === "overview" ? (
<GuardrailsOverview
accessToken={accessToken}
startDate={startDate}
endDate={endDate}
onSelectGuardrail={handleSelectGuardrail}
dateRangeControl={dateRangeControl}
/>
) : (
<GuardrailDetail
guardrailId={view.guardrailId}
onBack={handleBack}
accessToken={accessToken}
startDate={startDate}
endDate={endDate}
/>
<>
<div className="mb-4 flex items-center justify-end">{dateRangeControl}</div>
<GuardrailDetail
guardrailId={view.guardrailId}
onBack={handleBack}
accessToken={accessToken}
startDate={startDate}
endDate={endDate}
/>
</>
)}
</div>
</main>
);
}

View file

@ -110,6 +110,8 @@ describe("GuardrailsOverview", () => {
expect(await screen.findByRole("heading", { name: "Guardrails Monitor", level: 1 })).toBeInTheDocument();
expect(screen.getByText("Monitor guardrail performance across all requests")).toBeInTheDocument();
expect(document.querySelector(".lucide-heart-pulse")).not.toBeNull();
expect(screen.getByText("Monitor guardrail performance across all requests")).toBeInTheDocument();
expect(screen.getByRole("button", { name: /Export Data/i })).toBeInTheDocument();
});

View file

@ -1,11 +1,12 @@
import { useQuery } from "@tanstack/react-query";
import type { ColumnDef, OnChangeFn, SortingState } from "@tanstack/react-table";
import { Download, Settings, Shield, TrendingUp, TriangleAlert } from "lucide-react";
import { Download, HeartPulse, Settings, TrendingUp, TriangleAlert } from "lucide-react";
import React, { useMemo, useState } from "react";
import { DataTable, DataTableSortHeader } from "@/components/shared/DataTable";
import { getGuardrailsUsageOverview } from "@/components/networking";
import { type PerformanceRow } from "@/components/GuardrailsMonitor/mockData";
import { Button } from "@/components/ui/button";
import { PageHeader } from "@/components/shared/PageHeader";
import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";
import { EvaluationSettingsModal } from "./EvaluationSettingsModal";
import { MetricCard } from "@/components/GuardrailsMonitor/MetricCard";
@ -16,6 +17,7 @@ interface GuardrailsOverviewProps {
startDate: string;
endDate: string;
onSelectGuardrail: (id: string) => void;
dateRangeControl?: React.ReactNode;
}
type SortKey = "failRate" | "requestsEvaluated" | "avgLatency" | "falsePositiveRate" | "falseNegativeRate";
@ -43,6 +45,7 @@ export function GuardrailsOverview({
startDate,
endDate,
onSelectGuardrail,
dateRangeControl,
}: GuardrailsOverviewProps) {
const [sortBy, setSortBy] = useState<SortKey>("failRate");
const [sortDir, setSortDir] = useState<"asc" | "desc">("desc");
@ -197,23 +200,22 @@ export function GuardrailsOverview({
return (
<div>
<div className="flex items-start justify-between mb-5">
<div>
<div className="flex items-center gap-2 mb-1">
<Shield className="size-5 text-indigo-500" />
<h1 className="text-xl font-semibold text-foreground">Guardrails Monitor</h1>
</div>
<p className="text-sm text-muted-foreground">Monitor guardrail performance across all requests</p>
</div>
<div className="flex items-center gap-3">
<Button variant="outline" title="Coming soon">
<Download className="size-4" />
Export Data
</Button>
</div>
</div>
<PageHeader
icon={<HeartPulse />}
title="Guardrails Monitor"
subtitle="Monitor guardrail performance across all requests"
utilities={
<>
{dateRangeControl}
<Button variant="outline" title="Coming soon">
<Download className="size-4" />
Export Data
</Button>
</>
}
/>
<div className="mb-6 grid grid-cols-[repeat(auto-fit,minmax(7rem,1fr))] gap-4">
<div className="mt-6 mb-6 grid grid-cols-[repeat(auto-fit,minmax(7rem,1fr))] gap-4">
<MetricCard label="Total Evaluations" value={metrics.totalRequests.toLocaleString()} />
<MetricCard
label="Blocked Requests"

View file

@ -82,6 +82,8 @@ describe("ProjectsPage", () => {
mockUseProjects.mockReturnValue({ data: [], isLoading: false });
renderWithProviders(<ProjectsPage />);
expect(screen.getByRole("heading", { name: /projects/i })).toBeInTheDocument();
expect(screen.getByText("Manage projects within your teams")).toBeInTheDocument();
expect(document.querySelector(".lucide-folder")).not.toBeNull();
});
it("should show a 'Create Project' button", () => {

View file

@ -1,9 +1,9 @@
import { useProjects } from "@/app/(dashboard)/hooks/projects/useProjects";
import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams";
import { Plus, SearchIcon, X } from "lucide-react";
import { Folder, Plus, SearchIcon, X } from "lucide-react";
import { parseAsString, useQueryState } from "nuqs";
import { useMemo, useState } from "react";
import { LegacyPageHeader } from "@/components/shared/LegacyPageHeader";
import { PageHeader } from "@/components/shared/PageHeader";
import { Button } from "@/components/ui/button";
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@/components/ui/input-group";
import { CreateProjectModal } from "./ProjectModals/CreateProjectModal";
@ -54,21 +54,20 @@ export function ProjectsPage() {
}
return (
<div className="p-6 px-12">
<div className="mb-4">
<LegacyPageHeader
title="Projects"
subtitle="Manage projects within your teams"
actions={
<Button onClick={() => setIsCreateModalVisible(true)}>
<Plus className="size-4" />
Create Project
</Button>
}
/>
</div>
<div className="p-8">
<PageHeader
icon={<Folder />}
title="Projects"
subtitle="Manage projects within your teams"
primaryAction={
<Button onClick={() => setIsCreateModalVisible(true)}>
<Plus className="size-4" />
Create Project
</Button>
}
/>
<div className="mb-3 flex items-center">
<div className="mt-6 mb-3 flex items-center">
<InputGroup className="max-w-[400px]">
<InputGroupAddon>
<SearchIcon className="size-4 text-muted-foreground" />

View file

@ -207,6 +207,8 @@ it("left-anchors the create-key CTA below the title, between the header and the
renderWithProviders(<VirtualKeysTable headerActions={<button>Create New Key</button>} />);
const heading = screen.getByRole("heading", { name: "Virtual Keys" });
expect(screen.getByText("Every key that authenticates requests to the gateway.")).toBeInTheDocument();
expect(document.querySelector(".lucide-key-round")).not.toBeNull();
const ctas = screen.getAllByRole("button", { name: "Create New Key" });
expect(ctas).toHaveLength(1);
const cta = ctas[0];

View file

@ -12,7 +12,7 @@ import {
DataTableToolbar,
} from "@/components/shared/DataTable";
import { SearchSelect } from "@/components/shared/SearchSelect";
import { LegacyPageHeader } from "@/components/shared/LegacyPageHeader";
import { PageHeader } from "@/components/shared/PageHeader";
import { Input } from "@/components/ui/input";
import { useDebouncedValue } from "@tanstack/react-pacer/debouncer";
import { ColumnFiltersState, OnChangeFn, PaginationState, SortingState } from "@tanstack/react-table";
@ -182,13 +182,13 @@ export function VirtualKeysTable({ headerActions }: VirtualKeysTableProps) {
}
return (
<div className="flex h-full flex-col gap-4 overflow-hidden py-2">
<LegacyPageHeader
icon={<KeyRound className="size-5" />}
<div className="flex h-full flex-col gap-6 overflow-hidden">
<PageHeader
icon={<KeyRound />}
title="Virtual Keys"
subtitle="Every key that authenticates requests to the gateway."
primaryAction={headerActions}
/>
{headerActions}
<DataTable
data={keyList}
columns={columns}

View file

@ -1,33 +0,0 @@
import { renderWithProviders, screen } from "@/../tests/test-utils";
import { describe, expect, it } from "vitest";
import { LegacyPageHeader } from "./LegacyPageHeader";
describe("LegacyPageHeader", () => {
it("should render the title as a heading", () => {
renderWithProviders(<LegacyPageHeader title="Virtual Keys" />);
expect(screen.getByRole("heading", { name: "Virtual Keys" })).toBeInTheDocument();
});
it("should render the optional identity and actions", () => {
renderWithProviders(
<LegacyPageHeader
title="Virtual Keys"
subtitle="Every key that authenticates requests"
icon={<span>Key icon</span>}
actions={<button>Create New Key</button>}
/>,
);
expect(screen.getByText("Every key that authenticates requests")).toBeInTheDocument();
expect(screen.getByText("Key icon")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Create New Key" })).toBeInTheDocument();
});
it("should omit optional actions when none are provided", () => {
renderWithProviders(<LegacyPageHeader title="Virtual Keys" />);
expect(screen.queryByRole("button")).not.toBeInTheDocument();
});
});

View file

@ -1,25 +0,0 @@
"use client";
import * as React from "react";
interface LegacyPageHeaderProps {
title: React.ReactNode;
subtitle?: React.ReactNode;
icon?: React.ReactNode;
actions?: React.ReactNode;
}
export function LegacyPageHeader({ title, subtitle, icon, actions }: LegacyPageHeaderProps) {
return (
<div className="flex flex-wrap items-start justify-between gap-4">
<div className="flex items-center gap-2.5">
{icon != null && <span className="flex flex-none items-center text-foreground">{icon}</span>}
<div className="min-w-0">
<h1 className="text-xl font-semibold tracking-tight text-foreground">{title}</h1>
{subtitle != null && <p className="mt-0.5 text-sm text-muted-foreground">{subtitle}</p>}
</div>
</div>
{actions != null && <div className="flex items-center gap-2">{actions}</div>}
</div>
);
}

View file

@ -216,27 +216,25 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
const canCreateKey = userRole !== "Admin Viewer" && userRole !== "proxy_admin_viewer";
return (
<div className="mx-4 h-[75vh]">
<div className="grid grid-cols-1 gap-2 p-8 w-full mt-2">
<div className="col-span-1 flex flex-col gap-2">
<VirtualKeysTable
headerActions={
canCreateKey ? (
<CreateKey
key={selectedTeam ? selectedTeam.team_id : null}
team={selectedTeam as Team | null}
teams={teams as Team[]}
data={keys}
addKey={addKey}
autoOpenCreate={autoOpenCreate}
prefillData={prefillData}
/>
) : undefined
}
/>
</div>
<main className="h-[75vh] p-8">
<div className="flex h-full flex-col">
<VirtualKeysTable
headerActions={
canCreateKey ? (
<CreateKey
key={selectedTeam ? selectedTeam.team_id : null}
team={selectedTeam as Team | null}
teams={teams as Team[]}
data={keys}
addKey={addKey}
autoOpenCreate={autoOpenCreate}
prefillData={prefillData}
/>
) : undefined
}
/>
</div>
</div>
</main>
);
};