pdated settings/git-projects/
This commit is contained in:
parent
59e739768f
commit
809975cb76
|
|
@ -28,6 +28,7 @@ import { useAuth } from "@/auth/clerk";
|
||||||
import { ForgejoConnectionsTable } from "@/components/git/ForgejoConnectionsTable";
|
import { ForgejoConnectionsTable } from "@/components/git/ForgejoConnectionsTable";
|
||||||
import { ForgejoRepositoriesTable } from "@/components/git/ForgejoRepositoriesTable";
|
import { ForgejoRepositoriesTable } from "@/components/git/ForgejoRepositoriesTable";
|
||||||
import { DashboardPageLayout } from "@/components/templates/DashboardPageLayout";
|
import { DashboardPageLayout } from "@/components/templates/DashboardPageLayout";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ConfirmActionDialog } from "@/components/ui/confirm-action-dialog";
|
import { ConfirmActionDialog } from "@/components/ui/confirm-action-dialog";
|
||||||
import {
|
import {
|
||||||
|
|
@ -37,6 +38,7 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { getApiBaseUrl } from "@/lib/api-base";
|
import { getApiBaseUrl } from "@/lib/api-base";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
deleteForgejoConnection,
|
deleteForgejoConnection,
|
||||||
deleteForgejoRepository,
|
deleteForgejoRepository,
|
||||||
|
|
@ -71,6 +73,72 @@ type AttentionItem = {
|
||||||
href?: string;
|
href?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type SettingsTone = "accent" | "success" | "warning" | "danger" | "neutral";
|
||||||
|
|
||||||
|
const toneStyles: Record<
|
||||||
|
SettingsTone,
|
||||||
|
{
|
||||||
|
panel: string;
|
||||||
|
icon: string;
|
||||||
|
rail: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
accent: {
|
||||||
|
panel:
|
||||||
|
"border-[color:rgba(96,165,250,0.24)] bg-[linear-gradient(145deg,rgba(96,165,250,0.1),var(--surface)_58%)]",
|
||||||
|
icon: "border-[color:rgba(96,165,250,0.3)] bg-[color:var(--accent-soft)] text-[color:var(--accent-strong)]",
|
||||||
|
rail: "bg-[linear-gradient(90deg,rgba(96,165,250,0),rgba(96,165,250,0.82),rgba(96,165,250,0))]",
|
||||||
|
text: "text-[color:var(--accent-strong)]",
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
panel:
|
||||||
|
"border-[color:rgba(52,211,153,0.22)] bg-[linear-gradient(145deg,rgba(52,211,153,0.09),var(--surface)_58%)]",
|
||||||
|
icon: "border-[color:rgba(52,211,153,0.3)] bg-[color:rgba(52,211,153,0.14)] text-[color:var(--success)]",
|
||||||
|
rail: "bg-[linear-gradient(90deg,rgba(52,211,153,0),rgba(52,211,153,0.78),rgba(52,211,153,0))]",
|
||||||
|
text: "text-[color:var(--success)]",
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
panel:
|
||||||
|
"border-[color:rgba(251,191,36,0.24)] bg-[linear-gradient(145deg,rgba(251,191,36,0.09),var(--surface)_58%)]",
|
||||||
|
icon: "border-[color:rgba(251,191,36,0.32)] bg-[color:rgba(251,191,36,0.14)] text-[color:var(--warning)]",
|
||||||
|
rail: "bg-[linear-gradient(90deg,rgba(251,191,36,0),rgba(251,191,36,0.82),rgba(251,191,36,0))]",
|
||||||
|
text: "text-[color:var(--warning)]",
|
||||||
|
},
|
||||||
|
danger: {
|
||||||
|
panel:
|
||||||
|
"border-[color:rgba(248,113,113,0.24)] bg-[linear-gradient(145deg,rgba(248,113,113,0.09),var(--surface)_58%)]",
|
||||||
|
icon: "border-[color:rgba(248,113,113,0.3)] bg-[color:rgba(248,113,113,0.12)] text-[color:var(--danger)]",
|
||||||
|
rail: "bg-[linear-gradient(90deg,rgba(248,113,113,0),rgba(248,113,113,0.78),rgba(248,113,113,0))]",
|
||||||
|
text: "text-[color:var(--danger)]",
|
||||||
|
},
|
||||||
|
neutral: {
|
||||||
|
panel:
|
||||||
|
"border-[color:var(--border)] bg-[linear-gradient(180deg,rgba(255,255,255,0.035),rgba(255,255,255,0)_72%),var(--surface)]",
|
||||||
|
icon: "border-[color:var(--border-strong)] bg-[color:var(--surface-muted)] text-muted",
|
||||||
|
rail: "bg-[linear-gradient(90deg,rgba(96,165,250,0),rgba(96,165,250,0.28),rgba(52,211,153,0.2),rgba(96,165,250,0))]",
|
||||||
|
text: "text-muted",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function ToneRail({ tone }: { tone: SettingsTone }) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"pointer-events-none absolute inset-x-4 top-0 h-px",
|
||||||
|
toneStyles[tone].rail,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function panelClass(tone: SettingsTone = "neutral") {
|
||||||
|
return cn(
|
||||||
|
"relative overflow-hidden rounded-xl border p-4 shadow-lush md:p-5",
|
||||||
|
toneStyles[tone].panel,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const repositoryName = (repository: ForgejoRepository) =>
|
const repositoryName = (repository: ForgejoRepository) =>
|
||||||
repository.display_name || `${repository.owner}/${repository.repo}`;
|
repository.display_name || `${repository.owner}/${repository.repo}`;
|
||||||
|
|
||||||
|
|
@ -114,16 +182,23 @@ function SectionHeader({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
action,
|
action,
|
||||||
|
tone = "accent",
|
||||||
}: {
|
}: {
|
||||||
icon: ReactNode;
|
icon: ReactNode;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
action?: ReactNode;
|
action?: ReactNode;
|
||||||
|
tone?: SettingsTone;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
<div className="flex min-w-0 items-start gap-3">
|
<div className="flex min-w-0 items-start gap-3">
|
||||||
<div className="shrink-0 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-2 text-[color:var(--accent)]">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"shrink-0 rounded-lg border p-2",
|
||||||
|
toneStyles[tone].icon,
|
||||||
|
)}
|
||||||
|
>
|
||||||
{icon}
|
{icon}
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
|
|
@ -141,14 +216,22 @@ function StatCard({
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
caption,
|
caption,
|
||||||
|
tone = "accent",
|
||||||
}: {
|
}: {
|
||||||
icon: ReactNode;
|
icon: ReactNode;
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
caption: string;
|
caption: string;
|
||||||
|
tone?: SettingsTone;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"relative overflow-hidden rounded-xl border p-4 shadow-lush",
|
||||||
|
toneStyles[tone].panel,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ToneRail tone={tone} />
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex items-start justify-between gap-3">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted">
|
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted">
|
||||||
|
|
@ -158,7 +241,7 @@ function StatCard({
|
||||||
{value}
|
{value}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-2 text-[color:var(--accent)]">
|
<div className={cn("rounded-lg border p-2", toneStyles[tone].icon)}>
|
||||||
{icon}
|
{icon}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -190,11 +273,32 @@ function HealthSummaryPanel({
|
||||||
: status === "warning"
|
: status === "warning"
|
||||||
? "border-[color:rgba(245,158,11,0.35)] bg-[color:rgba(245,158,11,0.08)] text-[color:var(--warning)]"
|
? "border-[color:rgba(245,158,11,0.35)] bg-[color:rgba(245,158,11,0.08)] text-[color:var(--warning)]"
|
||||||
: "border-[color:rgba(248,113,113,0.35)] bg-[color:rgba(248,113,113,0.08)] text-[color:var(--danger)]";
|
: "border-[color:rgba(248,113,113,0.35)] bg-[color:rgba(248,113,113,0.08)] text-[color:var(--danger)]";
|
||||||
|
const metricTones: SettingsTone[] = [
|
||||||
|
"accent",
|
||||||
|
"success",
|
||||||
|
"warning",
|
||||||
|
"danger",
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5">
|
<section
|
||||||
|
className={cn(
|
||||||
|
"relative overflow-hidden rounded-xl border border-[color:rgba(96,165,250,0.28)] bg-[linear-gradient(135deg,rgba(96,165,250,0.16),rgba(52,211,153,0.09)_34%,rgba(251,191,36,0.08)_68%,var(--surface)_100%)] p-4 shadow-lush md:p-6",
|
||||||
|
status === "danger" &&
|
||||||
|
"border-[color:rgba(248,113,113,0.28)] bg-[linear-gradient(135deg,rgba(248,113,113,0.12),rgba(96,165,250,0.1)_42%,var(--surface)_100%)]",
|
||||||
|
status === "warning" &&
|
||||||
|
"border-[color:rgba(251,191,36,0.28)] bg-[linear-gradient(135deg,rgba(251,191,36,0.12),rgba(96,165,250,0.1)_42%,var(--surface)_100%)]",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="pointer-events-none absolute inset-x-0 top-0 h-px bg-[linear-gradient(90deg,rgba(96,165,250,0),rgba(96,165,250,0.85),rgba(52,211,153,0.85),rgba(251,191,36,0))]" />
|
||||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
|
<Badge
|
||||||
|
variant="accent"
|
||||||
|
className="mb-3 w-fit shadow-[0_0_24px_rgba(96,165,250,0.16)]"
|
||||||
|
>
|
||||||
|
Git operations
|
||||||
|
</Badge>
|
||||||
<div
|
<div
|
||||||
className={`inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-semibold uppercase tracking-wide ${statusStyles}`}
|
className={`inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-semibold uppercase tracking-wide ${statusStyles}`}
|
||||||
>
|
>
|
||||||
|
|
@ -229,11 +333,15 @@ function HealthSummaryPanel({
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5 grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
<div className="mt-5 grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||||
{metrics.map((metric) => (
|
{metrics.map((metric, index) => (
|
||||||
<div
|
<div
|
||||||
key={metric.label}
|
key={metric.label}
|
||||||
className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3"
|
className={cn(
|
||||||
|
"relative overflow-hidden rounded-lg border p-3",
|
||||||
|
toneStyles[metricTones[index % metricTones.length]].panel,
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
|
<ToneRail tone={metricTones[index % metricTones.length]} />
|
||||||
<p className="text-xs font-semibold uppercase tracking-wide text-muted">
|
<p className="text-xs font-semibold uppercase tracking-wide text-muted">
|
||||||
{metric.label}
|
{metric.label}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -281,11 +389,35 @@ function AttentionPanel({
|
||||||
const hasWebhookItems = items.some((item) => item.kind === "webhook");
|
const hasWebhookItems = items.some((item) => item.kind === "webhook");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5">
|
<section
|
||||||
|
className={panelClass(
|
||||||
|
items.some((item) => item.tone === "danger")
|
||||||
|
? "danger"
|
||||||
|
: items.length
|
||||||
|
? "warning"
|
||||||
|
: "success",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ToneRail
|
||||||
|
tone={
|
||||||
|
items.some((item) => item.tone === "danger")
|
||||||
|
? "danger"
|
||||||
|
: items.length
|
||||||
|
? "warning"
|
||||||
|
: "success"
|
||||||
|
}
|
||||||
|
/>
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
icon={<ListChecks className="h-4 w-4" />}
|
icon={<ListChecks className="h-4 w-4" />}
|
||||||
title="Needs Attention"
|
title="Needs Attention"
|
||||||
description="Connection and repository signals that can block fresh issue data."
|
description="Connection and repository signals that can block fresh issue data."
|
||||||
|
tone={
|
||||||
|
items.some((item) => item.tone === "danger")
|
||||||
|
? "danger"
|
||||||
|
: items.length
|
||||||
|
? "warning"
|
||||||
|
: "success"
|
||||||
|
}
|
||||||
action={
|
action={
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{hasSyncItems ? (
|
{hasSyncItems ? (
|
||||||
|
|
@ -340,7 +472,7 @@ function AttentionPanel({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={group.key}
|
key={group.key}
|
||||||
className="rounded-lg border border-[color:var(--border)]"
|
className="rounded-lg border border-[color:var(--border)] bg-[color:rgba(15,23,36,0.22)]"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between border-b border-[color:var(--border)] bg-[color:var(--surface-muted)] px-3 py-2">
|
<div className="flex items-center justify-between border-b border-[color:var(--border)] bg-[color:var(--surface-muted)] px-3 py-2">
|
||||||
<p className="text-xs font-semibold uppercase tracking-wide text-muted">
|
<p className="text-xs font-semibold uppercase tracking-wide text-muted">
|
||||||
|
|
@ -393,11 +525,13 @@ function LastImportPanel({
|
||||||
}) {
|
}) {
|
||||||
const lastImport = importRuns[0] ?? null;
|
const lastImport = importRuns[0] ?? null;
|
||||||
return (
|
return (
|
||||||
<section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5">
|
<section className={panelClass("accent")}>
|
||||||
|
<ToneRail tone="accent" />
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
icon={<Download className="h-4 w-4" />}
|
icon={<Download className="h-4 w-4" />}
|
||||||
title="Full Import"
|
title="Full Import"
|
||||||
description="Run a complete issue pull when webhooks or scheduled sync need a reset."
|
description="Run a complete issue pull when webhooks or scheduled sync need a reset."
|
||||||
|
tone="accent"
|
||||||
action={
|
action={
|
||||||
<Button type="button" variant="outline" size="sm" onClick={onOpen}>
|
<Button type="button" variant="outline" size="sm" onClick={onOpen}>
|
||||||
<Download className="h-4 w-4" />
|
<Download className="h-4 w-4" />
|
||||||
|
|
@ -1141,6 +1275,7 @@ export default function GitProjectSettingsPage() {
|
||||||
title="Git Project Settings"
|
title="Git Project Settings"
|
||||||
description="Manage Forgejo connections, tracked repositories, and issue sync."
|
description="Manage Forgejo connections, tracked repositories, and issue sync."
|
||||||
stickyHeader
|
stickyHeader
|
||||||
|
mainClassName="relative bg-app"
|
||||||
headerActions={
|
headerActions={
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Link href="/git-projects/issues">
|
<Link href="/git-projects/issues">
|
||||||
|
|
@ -1192,7 +1327,9 @@ export default function GitProjectSettingsPage() {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="space-y-6">
|
<div className="pointer-events-none absolute inset-x-0 top-0 h-72 bg-[linear-gradient(135deg,rgba(96,165,250,0.16),rgba(52,211,153,0.1)_38%,rgba(251,191,36,0.08)_64%,rgba(96,165,250,0)_100%)] blur-2xl" />
|
||||||
|
|
||||||
|
<div className="relative space-y-6">
|
||||||
{notice ? <NoticeBanner notice={notice} /> : null}
|
{notice ? <NoticeBanner notice={notice} /> : null}
|
||||||
{error ? (
|
{error ? (
|
||||||
<div className="flex items-start gap-3 rounded-xl border border-[color:rgba(248,113,113,0.35)] bg-[color:rgba(248,113,113,0.08)] p-3 text-sm text-[color:var(--danger)]">
|
<div className="flex items-start gap-3 rounded-xl border border-[color:rgba(248,113,113,0.35)] bg-[color:rgba(248,113,113,0.08)] p-3 text-sm text-[color:var(--danger)]">
|
||||||
|
|
@ -1247,11 +1384,13 @@ export default function GitProjectSettingsPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Connections */}
|
{/* Connections */}
|
||||||
<section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5">
|
<section className={panelClass("accent")}>
|
||||||
|
<ToneRail tone="accent" />
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
icon={<Link2 className="h-4 w-4" />}
|
icon={<Link2 className="h-4 w-4" />}
|
||||||
title="Forgejo Connections"
|
title="Forgejo Connections"
|
||||||
description="URL and token records used by tracked repositories."
|
description="URL and token records used by tracked repositories."
|
||||||
|
tone="accent"
|
||||||
action={
|
action={
|
||||||
<Link href="/git-projects/connections">
|
<Link href="/git-projects/connections">
|
||||||
<Button size="sm" variant="outline">
|
<Button size="sm" variant="outline">
|
||||||
|
|
@ -1274,11 +1413,13 @@ export default function GitProjectSettingsPage() {
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Repositories */}
|
{/* Repositories */}
|
||||||
<section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5">
|
<section className={panelClass("success")}>
|
||||||
|
<ToneRail tone="success" />
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
icon={<GitBranch className="h-4 w-4" />}
|
icon={<GitBranch className="h-4 w-4" />}
|
||||||
title="Tracked Repositories"
|
title="Tracked Repositories"
|
||||||
description="Repositories whose issues are cached and shown in Pipeline."
|
description="Repositories whose issues are cached and shown in Pipeline."
|
||||||
|
tone="success"
|
||||||
action={
|
action={
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Link href="/git-projects/repositories/new">
|
<Link href="/git-projects/repositories/new">
|
||||||
|
|
@ -1313,12 +1454,14 @@ export default function GitProjectSettingsPage() {
|
||||||
{/* Webhook Setup */}
|
{/* Webhook Setup */}
|
||||||
<section
|
<section
|
||||||
id="git-project-webhooks"
|
id="git-project-webhooks"
|
||||||
className="scroll-mt-24 rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5"
|
className={cn("scroll-mt-24", panelClass("warning"))}
|
||||||
>
|
>
|
||||||
|
<ToneRail tone="warning" />
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
icon={<Webhook className="h-4 w-4" />}
|
icon={<Webhook className="h-4 w-4" />}
|
||||||
title="Webhook Setup"
|
title="Webhook Setup"
|
||||||
description="Configure Forgejo webhooks to push issue updates to Pipeline in real time."
|
description="Configure Forgejo webhooks to push issue updates to Pipeline in real time."
|
||||||
|
tone="warning"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{repositories.length === 0 ? (
|
{repositories.length === 0 ? (
|
||||||
|
|
@ -1415,11 +1558,13 @@ export default function GitProjectSettingsPage() {
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Scheduled Sync */}
|
{/* Scheduled Sync */}
|
||||||
<section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5">
|
<section className={panelClass("neutral")}>
|
||||||
|
<ToneRail tone="neutral" />
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
icon={<Clock className="h-4 w-4" />}
|
icon={<Clock className="h-4 w-4" />}
|
||||||
title="Scheduled Sync"
|
title="Scheduled Sync"
|
||||||
description="Pipeline runs a background sync for all active repositories every 60 minutes."
|
description="Pipeline runs a background sync for all active repositories every 60 minutes."
|
||||||
|
tone="neutral"
|
||||||
/>
|
/>
|
||||||
<div className="mt-4 pl-0 sm:pl-11">
|
<div className="mt-4 pl-0 sm:pl-11">
|
||||||
<p className="text-sm text-muted">
|
<p className="text-sm text-muted">
|
||||||
|
|
|
||||||
|
|
@ -594,26 +594,10 @@ export default function SettingsPage() {
|
||||||
forceRedirectUrl: "/settings",
|
forceRedirectUrl: "/settings",
|
||||||
signUpForceRedirectUrl: "/settings",
|
signUpForceRedirectUrl: "/settings",
|
||||||
}}
|
}}
|
||||||
title="Settings"
|
title={null}
|
||||||
description="Manage your profile, workspace configuration, and integrations."
|
|
||||||
stickyHeader
|
stickyHeader
|
||||||
headerActions={
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
<Link href="/settings/ai-providers">
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
<KeyRound className="h-4 w-4" />
|
|
||||||
AI Providers
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Link href="/settings/git-projects">
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
<GitBranch className="h-4 w-4" />
|
|
||||||
Git Settings
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
mainClassName="relative bg-app"
|
mainClassName="relative bg-app"
|
||||||
|
headerClassName="hidden"
|
||||||
>
|
>
|
||||||
<div className="pointer-events-none absolute inset-x-0 top-0 h-72 bg-[linear-gradient(135deg,rgba(96,165,250,0.16),rgba(52,211,153,0.1)_38%,rgba(251,191,36,0.08)_64%,rgba(96,165,250,0)_100%)] blur-2xl" />
|
<div className="pointer-events-none absolute inset-x-0 top-0 h-72 bg-[linear-gradient(135deg,rgba(96,165,250,0.16),rgba(52,211,153,0.1)_38%,rgba(251,191,36,0.08)_64%,rgba(96,165,250,0)_100%)] blur-2xl" />
|
||||||
|
|
||||||
|
|
@ -666,8 +650,8 @@ export default function SettingsPage() {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div className="grid gap-6 lg:grid-cols-[280px_minmax(0,1fr)]">
|
<div className="grid gap-6 lg:grid-cols-[minmax(0,1fr)_280px]">
|
||||||
<aside className="lg:sticky lg:top-28 lg:self-start">
|
<aside className="order-2 lg:sticky lg:top-8 lg:self-start">
|
||||||
<nav className="relative overflow-hidden rounded-xl border border-[color:var(--border)] bg-[linear-gradient(180deg,rgba(255,255,255,0.035),rgba(255,255,255,0)_72%),var(--surface)] p-3 shadow-lush">
|
<nav className="relative overflow-hidden rounded-xl border border-[color:var(--border)] bg-[linear-gradient(180deg,rgba(255,255,255,0.035),rgba(255,255,255,0)_72%),var(--surface)] p-3 shadow-lush">
|
||||||
<span className="pointer-events-none absolute inset-x-4 top-0 h-px bg-[linear-gradient(90deg,rgba(96,165,250,0),rgba(96,165,250,0.46),rgba(52,211,153,0.32),rgba(96,165,250,0))]" />
|
<span className="pointer-events-none absolute inset-x-4 top-0 h-px bg-[linear-gradient(90deg,rgba(96,165,250,0),rgba(96,165,250,0.46),rgba(52,211,153,0.32),rgba(96,165,250,0))]" />
|
||||||
<p className="px-2 pb-2 pt-1 text-xs font-semibold uppercase tracking-wider text-muted">
|
<p className="px-2 pb-2 pt-1 text-xs font-semibold uppercase tracking-wider text-muted">
|
||||||
|
|
@ -712,7 +696,7 @@ export default function SettingsPage() {
|
||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div className="space-y-6">
|
<div className="order-1 space-y-6">
|
||||||
<div className="grid gap-6 xl:grid-cols-[minmax(0,1.3fr)_minmax(320px,0.7fr)]">
|
<div className="grid gap-6 xl:grid-cols-[minmax(0,1.3fr)_minmax(320px,0.7fr)]">
|
||||||
<SettingsPanel
|
<SettingsPanel
|
||||||
id="profile"
|
id="profile"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue