feat: add AI settings controls

This commit is contained in:
2026-04-10 15:40:29 -04:00
parent e01a7ed1ad
commit 12849b2362
16 changed files with 907 additions and 127 deletions

View File

@@ -71,6 +71,8 @@ function ShortcutsList({ os }: { os: Os }) {
// ─── Types ────────────────────────────────────────────────────────────────────
interface AIToolbarProps {
adminAiEnabled: boolean;
aiEnabled: boolean;
isAuthenticated: boolean;
isPending: boolean;
aiPrompt: string;
@@ -94,6 +96,8 @@ interface AIToolbarProps {
// ─── Component ────────────────────────────────────────────────────────────────
export const AIToolbar = ({
adminAiEnabled,
aiEnabled,
isAuthenticated,
isPending,
aiPrompt,
@@ -255,10 +259,28 @@ export const AIToolbar = ({
}
const hasImages = imagePreviews.length > 0;
const canUseAi = adminAiEnabled && aiEnabled;
const showDisabledState = isAuthenticated && !canUseAi;
return (
<div className="space-y-3">
{isAuthenticated ? (
{showDisabledState ? (
<div className="flex items-center gap-3 py-2">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground">
<Sparkles className="h-4 w-4" />
</div>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium leading-tight text-foreground">
AI integrations are unavailable
</p>
<p className="mt-0.5 text-sm leading-relaxed text-muted-foreground">
{adminAiEnabled
? "AI has been turned off in this browser from Settings."
: "AI integrations are currently disabled by the administrator."}
</p>
</div>
</div>
) : isAuthenticated ? (
<div className="space-y-4">
<div className="rounded-2xl border border-border/70 bg-background/90 shadow-sm focus-within:ring-2 focus-within:ring-primary/30">
<Textarea
@@ -316,7 +338,7 @@ export const AIToolbar = ({
size="sm"
className="h-7 max-w-full rounded-full px-2.5 text-[11px]"
onClick={() => onAiTemplateSelect(prompt)}
disabled={aiLoading}
disabled={aiLoading || !canUseAi}
>
<span className="truncate">{prompt}</span>
</Button>
@@ -371,7 +393,7 @@ export const AIToolbar = ({
<div className="flex items-center justify-between gap-2">
<ImagePicker
onFilesSelect={onImagesSelect}
disabled={aiLoading}
disabled={aiLoading || !canUseAi}
multiple
variant="ghost"
size="sm"
@@ -425,7 +447,7 @@ export const AIToolbar = ({
variant="ghost"
size="sm"
onClick={onAiSummarize}
disabled={aiLoading}
disabled={aiLoading || !canUseAi}
className="h-9 gap-1.5 rounded-xl px-3 text-xs text-muted-foreground hover:text-primary"
>
<Bot className="h-3 w-3" />
@@ -437,7 +459,9 @@ export const AIToolbar = ({
size="sm"
className="h-10 gap-1.5 rounded-xl px-4 text-xs"
onClick={onAiCreate}
disabled={aiLoading || (!aiPrompt.trim() && !hasImages)}
disabled={
aiLoading || !canUseAi || (!aiPrompt.trim() && !hasImages)
}
>
{aiLoading ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
@@ -456,11 +480,11 @@ export const AIToolbar = ({
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-foreground leading-tight">
Generate event drafts with AI
Sign in required to generate event drafts with AI
</p>
<p className="text-xs text-muted-foreground mt-0.5">
Paste natural language or a flyer, then review the filled event
before saving.
Sign in to turn natural language or flyers into event drafts, then
review or save them from your calendar workflow.
</p>
</div>
</div>