Commercial frontend cleanup: fix admin TypeError, simplify UI for end users, add Suspense boundaries

This commit is contained in:
2025-11-30 22:28:59 +01:00
parent 3346817a8a
commit d31a132808
7 changed files with 323 additions and 452 deletions

View File

@@ -323,104 +323,34 @@ export function FileUploader() {
<CardHeader>
<CardTitle className="text-white">Translation Options</CardTitle>
<CardDescription>
Configure your translation preferences
Select your target language and start translating
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Target Language */}
<div className="space-y-2">
<Label htmlFor="language" className="text-zinc-300">Target Language</Label>
<Select value={targetLanguage} onValueChange={setTargetLanguage}>
<SelectTrigger id="language" className="bg-zinc-800 border-zinc-700 text-white">
<SelectValue placeholder="Select language" />
</SelectTrigger>
<SelectContent className="bg-zinc-800 border-zinc-700">
{languages.map((lang) => (
<SelectItem
key={lang.code}
value={lang.code}
className="text-white hover:bg-zinc-700"
>
<span className="flex items-center gap-2">
<span>{lang.flag}</span>
<span>{lang.name}</span>
</span>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Provider */}
<div className="space-y-2">
<Label htmlFor="provider" className="text-zinc-300">Translation Provider</Label>
<Select value={provider} onValueChange={(value) => setProvider(value as ProviderType)}>
<SelectTrigger id="provider" className="bg-zinc-800 border-zinc-700 text-white">
<SelectValue placeholder="Select provider" />
</SelectTrigger>
<SelectContent className="bg-zinc-800 border-zinc-700">
{providers.map((prov) => (
<SelectItem
key={prov.id}
value={prov.id}
className="text-white hover:bg-zinc-700"
>
<span className="flex items-center gap-2">
<span>{prov.icon}</span>
<span>{prov.name}</span>
</span>
</SelectItem>
))}
</SelectContent>
</Select>
{/* Warning if API key not configured */}
{provider === "openai" && !settings.openaiApiKey && (
<p className="text-xs text-amber-400 mt-1">
OpenAI API key not configured. Go to Settings Translation Services
</p>
)}
{provider === "deepl" && !settings.deeplApiKey && (
<p className="text-xs text-amber-400 mt-1">
DeepL API key not configured. Go to Settings Translation Services
</p>
)}
{provider === "webllm" && !webllm.isLoaded && (
<p className="text-xs text-amber-400 mt-1">
WebLLM model not loaded. Go to Settings Translation Services to load a model
</p>
)}
{provider === "webllm" && webllm.isLoaded && (
<p className="text-xs text-green-400 mt-1 flex items-center gap-1">
<Cpu className="h-3 w-3" />
Model ready: {webllm.currentModel}
</p>
)}
{provider === "webllm" && !webllm.isWebGPUSupported() && (
<p className="text-xs text-red-400 mt-1 flex items-center gap-1">
<AlertTriangle className="h-3 w-3" />
WebGPU not supported in this browser
</p>
)}
</div>
{/* Target Language */}
<div className="space-y-2">
<Label htmlFor="language" className="text-zinc-300">Target Language</Label>
<Select value={targetLanguage} onValueChange={setTargetLanguage}>
<SelectTrigger id="language" className="bg-zinc-800 border-zinc-700 text-white">
<SelectValue placeholder="Select language" />
</SelectTrigger>
<SelectContent className="bg-zinc-800 border-zinc-700 max-h-80">
{languages.map((lang) => (
<SelectItem
key={lang.code}
value={lang.code}
className="text-white hover:bg-zinc-700"
>
<span className="flex items-center gap-2">
<span>{lang.flag}</span>
<span>{lang.name}</span>
</span>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Image Translation Toggle */}
{(provider === "ollama" || provider === "openai") && (
<div className="flex items-center justify-between rounded-lg border border-zinc-800 p-4">
<div className="space-y-0.5">
<Label className="text-zinc-300">Translate Images</Label>
<p className="text-xs text-zinc-500">
Extract and translate text from embedded images using vision model
</p>
</div>
<Switch
checked={translateImages}
onCheckedChange={setTranslateImages}
/>
</div>
)}
{/* Translate Button */}
<Button
onClick={handleTranslate}

View File

@@ -135,16 +135,16 @@ export function FeaturesSection() {
color: "text-purple-400",
},
{
icon: Server,
title: "Self-Host Option",
description: "Use your own Ollama server for complete privacy and unlimited usage",
color: "text-orange-400",
icon: Sparkles,
title: "AI-Powered",
description: "Advanced neural translation for natural, context-aware results",
color: "text-teal-400",
},
{
icon: Sparkles,
title: "Multiple AI Providers",
description: "Choose from Google, DeepL, OpenAI, or local Ollama models",
color: "text-teal-400",
icon: Server,
title: "Enterprise Ready",
description: "API access, team management, and dedicated support for businesses",
color: "text-orange-400",
},
];
@@ -186,7 +186,7 @@ export function PricingPreview() {
name: "Free",
price: "$0",
description: "Perfect for trying out",
features: ["3 documents/day", "10 pages/doc", "Ollama only"],
features: ["5 documents/day", "10 pages/doc", "Basic support"],
cta: "Get Started",
href: "/auth/register",
},
@@ -195,7 +195,7 @@ export function PricingPreview() {
price: "$29",
period: "/month",
description: "For professionals",
features: ["200 documents/month", "All providers", "API access", "Priority support"],
features: ["200 documents/month", "Unlimited pages", "Priority support", "API access"],
cta: "Start Free Trial",
href: "/pricing",
popular: true,
@@ -284,26 +284,5 @@ export function PricingPreview() {
}
export function SelfHostCTA() {
return (
<div className="py-16 px-4">
<div className="max-w-4xl mx-auto">
<div className="rounded-2xl border border-zinc-800 bg-gradient-to-r from-orange-500/10 to-amber-500/10 p-8 text-center">
<Server className="h-12 w-12 text-orange-400 mx-auto mb-4" />
<h2 className="text-2xl font-bold text-white mb-2">
Prefer Self-Hosting?
</h2>
<p className="text-zinc-400 mb-6 max-w-xl mx-auto">
Run your own Ollama server for complete privacy and unlimited translations.
No API costs, no quotas, your data stays on your machine.
</p>
<Link href="/ollama-setup">
<Button className="bg-orange-500 hover:bg-orange-600 text-white">
Setup Ollama
<ArrowRight className="ml-2 h-4 w-4" />
</Button>
</Link>
</div>
</div>
</div>
);
return null; // Removed for commercial version
}

View File

@@ -5,15 +5,11 @@ import { usePathname } from "next/navigation";
import { useState, useEffect, useCallback, memo } from "react";
import { cn } from "@/lib/utils";
import {
Settings,
Cloud,
BookText,
Upload,
LayoutDashboard,
LogIn,
Crown,
LogOut,
Server,
} from "lucide-react";
import {
Tooltip,
@@ -37,24 +33,6 @@ const navigation = [
icon: Upload,
description: "Translate documents",
},
{
name: "Translation Services",
href: "/settings/services",
icon: Cloud,
description: "Configure translation providers",
},
{
name: "Context & Glossary",
href: "/settings/context",
icon: BookText,
description: "System prompts and glossary",
},
{
name: "General Settings",
href: "/settings",
icon: Settings,
description: "Configure general settings",
},
];
const planColors: Record<string, string> = {
@@ -230,29 +208,6 @@ export function Sidebar() {
)}
</div>
)}
{/* Self-Host Option */}
<div className="mt-4 pt-4 border-t border-zinc-800">
<Tooltip>
<TooltipTrigger asChild>
<Link
href="/ollama-setup"
className={cn(
"flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors",
pathname === "/ollama-setup"
? "bg-orange-500/10 text-orange-400"
: "text-zinc-400 hover:bg-zinc-800 hover:text-zinc-100"
)}
>
<Server className="h-5 w-5" />
<span>Self-Host (Free)</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">
<p>Run your own Ollama for unlimited free translations</p>
</TooltipContent>
</Tooltip>
</div>
</nav>
{/* User section at bottom */}