feat: import API key, people card redesign, birthday popover, autocomplete portal
- Add import_api_key setting with auto-generate endpoint and settings UI - Import job runner uses import API key when available, falls back to session - Show ApiKeyGate empty state in both workflow and import pages when key missing - Replace workflow API key warning popover with full gate overlay - Redesign person cards: square avatar, hover action toolbar, cleaner layout - Birthday field now uses popover with calendar + natural language input - Fix autocomplete dropdown clipping inside overflow-hidden cards via portal - People grid column slider persisted to localStorage (min 3, max 20)
This commit is contained in:
1 parent
6a5b2b442a
commit
e47e99ccfc
10 files changed
+626
-266
No files matched your search
@@ -0,0 +1,83 @@
|
||||
import React, { useState } from "react";
|
||||
import { KeyRound, Wand2, Settings } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import Link from "next/link";
|
||||
|
||||
interface Permission {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface ApiKeyGateProps {
|
||||
title: string;
|
||||
description: string;
|
||||
permissions: Permission[];
|
||||
generateEndpoint: string;
|
||||
onGenerated: () => void;
|
||||
}
|
||||
|
||||
export default function ApiKeyGate({ title, description, permissions, generateEndpoint, onGenerated }: ApiKeyGateProps) {
|
||||
const [generating, setGenerating] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const handleGenerate = async () => {
|
||||
setGenerating(true);
|
||||
setError(null);
|
||||
try {
|
||||
const res = await fetch(generateEndpoint, { method: "POST" });
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
throw new Error(data.message ?? "Failed to generate API key");
|
||||
}
|
||||
onGenerated();
|
||||
} catch (err: any) {
|
||||
setError(err.message ?? "Failed to generate API key");
|
||||
} finally {
|
||||
setGenerating(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 items-center justify-center p-8">
|
||||
<div className="max-w-md w-full text-center space-y-6">
|
||||
<div className="mx-auto h-14 w-14 rounded-2xl bg-muted flex items-center justify-center">
|
||||
<KeyRound className="h-6 w-6 text-muted-foreground" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-lg font-semibold">{title}</h2>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">{description}</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border bg-muted/40 p-4 text-left space-y-2">
|
||||
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Required permissions</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{permissions.map((p) => (
|
||||
<Badge key={p.name} variant="outline" className="text-[11px] font-mono font-normal" title={p.description}>
|
||||
{p.name}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<p className="text-xs text-destructive">{error}</p>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<Button onClick={handleGenerate} disabled={generating} className="w-full">
|
||||
<Wand2 className="h-4 w-4 mr-2" />
|
||||
{generating ? "Generating…" : "Generate API key automatically"}
|
||||
</Button>
|
||||
<Link href="/settings">
|
||||
<Button variant="outline" className="w-full">
|
||||
<Settings className="h-4 w-4 mr-2" />
|
||||
Configure manually in Settings
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user