feat(web): add settings root index page with category cards #471
239
apps/web/src/app/(authenticated)/settings/page.tsx
Normal file
239
apps/web/src/app/(authenticated)/settings/page.tsx
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import type { ReactElement, ReactNode } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
interface CategoryConfig {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
href: string;
|
||||||
|
accent: string;
|
||||||
|
iconBg: string;
|
||||||
|
icon: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SettingsCategoryCardProps {
|
||||||
|
category: CategoryConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SettingsCategoryCard({ category }: SettingsCategoryCardProps): ReactElement {
|
||||||
|
const [hovered, setHovered] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={category.href} style={{ textDecoration: "none" }}>
|
||||||
|
<div
|
||||||
|
onMouseEnter={(): void => {
|
||||||
|
setHovered(true);
|
||||||
|
}}
|
||||||
|
onMouseLeave={(): void => {
|
||||||
|
setHovered(false);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
background: hovered ? "var(--surface-2)" : "var(--surface)",
|
||||||
|
border: `1px solid ${hovered ? category.accent : "var(--border)"}`,
|
||||||
|
borderRadius: "var(--r-lg)",
|
||||||
|
padding: 20,
|
||||||
|
transition: "background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease",
|
||||||
|
boxShadow: hovered ? "0 4px 16px rgba(0,0,0,0.2)" : "none",
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 12,
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Icon well */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: "var(--r)",
|
||||||
|
background: category.iconBg,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
color: category.accent,
|
||||||
|
transition: "transform 0.15s ease",
|
||||||
|
transform: hovered ? "scale(1.05)" : "scale(1)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{category.icon}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Title */}
|
||||||
|
<div style={{ fontSize: "1rem", fontWeight: 700, color: "var(--text)" }}>
|
||||||
|
{category.title}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: "0.83rem",
|
||||||
|
color: "var(--muted)",
|
||||||
|
lineHeight: 1.55,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{category.description}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: "0.83rem",
|
||||||
|
color: hovered ? category.accent : "var(--muted)",
|
||||||
|
fontWeight: 500,
|
||||||
|
marginTop: "auto",
|
||||||
|
transition: "color 0.15s ease",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Manage →
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const categories: CategoryConfig[] = [
|
||||||
|
{
|
||||||
|
title: "Credentials",
|
||||||
|
description:
|
||||||
|
"Securely store and manage API keys, tokens, and passwords used by agents and integrations.",
|
||||||
|
href: "/settings/credentials",
|
||||||
|
accent: "var(--ms-blue-400)",
|
||||||
|
iconBg: "rgba(47, 128, 255, 0.12)",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<rect x="5" y="9" width="10" height="8" rx="1.5" />
|
||||||
|
<path d="M7 9V6a3 3 0 0 1 6 0v3" />
|
||||||
|
<circle cx="10" cy="13" r="1" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Domains",
|
||||||
|
description:
|
||||||
|
"Organize tasks and projects by life areas or functional domains within your workspace.",
|
||||||
|
href: "/settings/domains",
|
||||||
|
accent: "var(--ms-teal-400)",
|
||||||
|
iconBg: "rgba(20, 184, 166, 0.12)",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<circle cx="10" cy="10" r="7.5" />
|
||||||
|
<line x1="2.5" y1="10" x2="17.5" y2="10" />
|
||||||
|
<path d="M10 2.5c2 2.5 3 5 3 7.5s-1 5-3 7.5" />
|
||||||
|
<path d="M10 2.5c-2 2.5-3 5-3 7.5s1 5 3 7.5" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "AI Personalities",
|
||||||
|
description:
|
||||||
|
"Customize how the AI assistant communicates \u2014 tone, formality, and response style.",
|
||||||
|
href: "/settings/personalities",
|
||||||
|
accent: "var(--ms-purple-400)",
|
||||||
|
iconBg: "rgba(139, 92, 246, 0.12)",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<circle cx="10" cy="6" r="3" />
|
||||||
|
<path d="M4 17c0-3.3 2.7-6 6-6s6 2.7 6 6" />
|
||||||
|
<path d="M14 10l1.5 1.5 3-3" stroke="currentColor" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Workspaces",
|
||||||
|
description:
|
||||||
|
"Create and manage workspaces to organize projects and collaborate with your team.",
|
||||||
|
href: "/settings/workspaces",
|
||||||
|
accent: "var(--ms-amber-400)",
|
||||||
|
iconBg: "rgba(245, 158, 11, 0.12)",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<circle cx="10" cy="10" r="2" />
|
||||||
|
<circle cx="4" cy="5" r="1.5" />
|
||||||
|
<circle cx="16" cy="5" r="1.5" />
|
||||||
|
<circle cx="16" cy="15" r="1.5" />
|
||||||
|
<line x1="8.3" y1="8.7" x2="5.3" y2="6.2" />
|
||||||
|
<line x1="11.7" y1="8.7" x2="14.7" y2="6.2" />
|
||||||
|
<line x1="11.7" y1="11.3" x2="14.7" y2="13.8" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function SettingsPage(): ReactElement {
|
||||||
|
return (
|
||||||
|
<div className="max-w-6xl mx-auto p-6">
|
||||||
|
{/* Page header */}
|
||||||
|
<div style={{ marginBottom: 24 }}>
|
||||||
|
<h1
|
||||||
|
style={{
|
||||||
|
fontSize: "1.875rem",
|
||||||
|
fontWeight: 700,
|
||||||
|
color: "var(--text)",
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Settings
|
||||||
|
</h1>
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
color: "var(--muted)",
|
||||||
|
margin: "8px 0 0 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Configure your workspace, credentials, and preferences
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Category grid */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
{categories.map((category) => (
|
||||||
|
<SettingsCategoryCard key={category.href} category={category} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user