"use client"; import { useEffect, useRef, useState } from "react"; import Image from "next/image"; import { Blocks, Brain, ImageIcon, Key, Monitor, Moon, Palette, Save, Shield, Sun, Trash2, Upload, User, } from "lucide-react"; import { loadSettings, removeBrandingAsset, saveAiSettings, saveColorMode, saveGeneralSettings, updatePassword, updateProfile, } from "./actions"; import { Button, Card, CardContent, Input, Label, RadioGroup, RadioGroupItem, } from "poyraz-ui/atoms"; import { toast } from "poyraz-ui/molecules"; import { applyColorMode } from "@/components/theme/color-mode-sync"; import { isColorMode, type ColorMode } from "@/lib/color-mode"; type AiProvider = "groq" | "ollama" | "openai" | "gemini"; type BrandingAsset = "lightLogo" | "darkLogo" | "favicon"; const colorModeOptions = [ { value: "light", label: "Açık", description: "Her zaman aydınlık renk paletini kullanır.", icon: Sun, }, { value: "dark", label: "Koyu", description: "Her zaman koyu renk paletini kullanır.", icon: Moon, }, { value: "system", label: "Sistem", description: "Cihazınızın görünüm tercihini otomatik takip eder.", icon: Monitor, }, ] satisfies Array<{ value: ColorMode; label: string; description: string; icon: typeof Sun; }>; export default function SettingsPage() { const [activeTab, setActiveTab] = useState("Genel"); // Profile States const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const [avatarUrl, setAvatarUrl] = useState(""); // Security States const formRef = useRef(null); // AI States const [aiProvider, setAiProvider] = useState("gemini"); const [apiKey, setApiKey] = useState(""); const [hasApiKey, setHasApiKey] = useState(false); const [colorMode, setColorMode] = useState("system"); const [isSavingColorMode, setIsSavingColorMode] = useState(false); const [workspaceName, setWorkspaceName] = useState("Neta"); const [metaTitle, setMetaTitle] = useState("Neta"); const [shortName, setShortName] = useState("Neta"); const [primaryColor, setPrimaryColor] = useState("#C81E1E"); const [assetUrls, setAssetUrls] = useState>({ lightLogo: "", darkLogo: "", favicon: "", }); const [pendingAssetUrls, setPendingAssetUrls] = useState>({ lightLogo: "", darkLogo: "", favicon: "", }); const [customAssets, setCustomAssets] = useState>({ lightLogo: false, darkLogo: false, favicon: false, }); const [isSavingBranding, setIsSavingBranding] = useState(false); const assetObjectUrlRefs = useRef>>({}); const tabs = [ { name: "Genel", icon: Palette }, { name: "Profile & Account", icon: User }, { name: "AI Preferences", icon: Brain }, { name: "Security", icon: Shield }, ]; useEffect(() => { let isActive = true; const fetchData = async () => { const settings = await loadSettings(); if (!isActive) return; setFirstName(settings.firstName); setLastName(settings.lastName); setAvatarUrl(settings.avatarUrl); setAiProvider(settings.aiProvider); setHasApiKey(settings.hasApiKey); setColorMode(settings.colorMode); setWorkspaceName(settings.workspaceName); setMetaTitle(settings.metaTitle); setShortName(settings.shortName); setPrimaryColor(settings.primaryColor); setAssetUrls({ lightLogo: settings.lightLogoUrl, darkLogo: settings.darkLogoUrl, favicon: settings.faviconUrl, }); setCustomAssets({ lightLogo: settings.hasCustomLightLogo, darkLogo: settings.hasCustomDarkLogo, favicon: settings.hasCustomFavicon, }); }; void fetchData(); return () => { isActive = false; }; }, []); useEffect(() => { const objectUrls = assetObjectUrlRefs.current; return () => { for (const objectUrl of Object.values(objectUrls)) { if (objectUrl) URL.revokeObjectURL(objectUrl); } }; }, []); const handleProfileAction = async (formData: FormData) => { const response = await updateProfile(formData); if (response?.error) { toast.error(`Hata: ${response.error}`); } else { toast.success("Profil güncellendi!"); const avatar = formData.get("avatar"); if (avatar instanceof File && avatar.size > 0) window.location.reload(); } }; const handlePasswordAction = async (formData: FormData) => { const response = await updatePassword(formData); if (response?.error) { toast.error(`Hata: ${response.error}`); } else { toast.success("Şifre güncellendi!"); formRef.current?.reset(); } }; const handleSaveAI = async () => { const response = await saveAiSettings(aiProvider, apiKey); if (response.error) { toast.error(response.error); return; } setHasApiKey(Boolean(response.hasApiKey)); setApiKey(""); toast.success("Yapay Zeka ayarları kaydedildi!"); }; const handleColorModeChange = async (value: string) => { if (!isColorMode(value) || value === colorMode || isSavingColorMode) return; const previousColorMode = colorMode; setColorMode(value); applyColorMode(value); setIsSavingColorMode(true); try { const response = await saveColorMode(value); if (response.error) { setColorMode(previousColorMode); applyColorMode(previousColorMode); toast.error(response.error); return; } toast.success("Görünüm tercihi kaydedildi."); } finally { setIsSavingColorMode(false); } }; const handleBrandingAssetChange = ( asset: BrandingAsset, event: React.ChangeEvent, ) => { const previousObjectUrl = assetObjectUrlRefs.current[asset]; if (previousObjectUrl) URL.revokeObjectURL(previousObjectUrl); const file = event.target.files?.[0]; const objectUrl = file ? URL.createObjectURL(file) : ""; assetObjectUrlRefs.current[asset] = objectUrl || undefined; setPendingAssetUrls((current) => ({ ...current, [asset]: objectUrl })); }; const handleGeneralSettingsAction = async (formData: FormData) => { setIsSavingBranding(true); try { const response = await saveGeneralSettings(formData); if (response.error) { toast.error(response.error); return; } toast.success("Genel görünüm ve marka ayarları güncellendi."); window.location.reload(); } finally { setIsSavingBranding(false); } }; const handleRemoveBrandingAsset = async (asset: BrandingAsset) => { setIsSavingBranding(true); try { const response = await removeBrandingAsset(asset); if (response.error) { toast.error(response.error); return; } toast.success("Marka görseli kaldırıldı."); window.location.reload(); } finally { setIsSavingBranding(false); } }; return (

Ayarlar

Workspace markanızı, profilinizi, görünümü ve sistem tercihlerinizi yönetin.

{/* Settings Sidebar */}
{tabs.map((tab) => { const Icon = tab.icon; return ( ) })}
{/* Settings Content Area */}
{activeTab === "Genel" && (

Genel görünüm ve marka

Web ve mobil istemcilerde kullanılan workspace kimliğini, marka görsellerini ve tema tercihlerini yönetin.

setWorkspaceName(event.target.value)} minLength={1} maxLength={120} required />

Firma, freelance marka veya çalışma alanı adınız.

setMetaTitle(event.target.value)} minLength={1} maxLength={80} required />

Sekme başlıklarında ve uygulama metadata bilgisinde kullanılır.

setShortName(event.target.value)} minLength={1} maxLength={24} required />

Mobil uygulama ve ana ekrana ekleme alanlarında kullanılan kısa ad.

Tarayıcı ikonu

Favicon, web manifest ve mobil instance metadata alanlarında kullanılır.

Bir renk seçin; vurgu, focus ve yumuşak yüzey tonları otomatik türetilir.

setPrimaryColor(event.target.value.toUpperCase())} aria-label="Ana renk seçici" className="h-11 w-16 shrink-0 cursor-pointer p-1" /> setPrimaryColor(event.target.value.toUpperCase())} pattern="^#[0-9A-Fa-f]{6}$" maxLength={7} placeholder="#C81E1E" required className="font-mono uppercase" />

Tema görünümü

Arayüzün açık, koyu veya cihazınızla uyumlu görünmesini seçin.

{colorModeOptions.map((option) => { const Icon = option.icon; const selected = colorMode === option.value; return ( ); })}

{isSavingColorMode ? "Görünüm tercihi kaydediliyor…" : "Değişiklik tüm sayfalara anında uygulanır."}

)} {activeTab === "Profile & Account" && (

Kullanıcı Profili

{avatarUrl ? ( Avatar ) : (
)}
setFirstName(e.target.value)} />
setLastName(e.target.value)} />
)} {activeTab === "Security" && (

Şifre İşlemleri

)} {activeTab === "AI Preferences" && (

AI Asistan Konfigürasyonu

Model ve Sağlayıcı Seçimi

{aiProvider !== "ollama" && (

API Keys

setApiKey(e.target.value)} placeholder={hasApiKey ? "Kayıtlı anahtarı korumak için boş bırakın" : "sk-..."} />
)}
)} {["Integrations", "Notifications", "Billing & Plans"].includes(activeTab) && (

{activeTab}

Bu bölüm şu an geliştirme aşamasındadır.

)}
); } type BrandingAssetFieldProps = { asset: BrandingAsset; inputId: string; name: string; title: string; description?: string; accept: string; currentUrl: string; pendingUrl: string; hasCustomAsset: boolean; previewTone: "light" | "dark" | "neutral"; compact?: boolean; disabled: boolean; onChange: (asset: BrandingAsset, event: React.ChangeEvent) => void; onRemove: (asset: BrandingAsset) => void; }; function BrandingAssetField({ asset, inputId, name, title, description, accept, currentUrl, pendingUrl, hasCustomAsset, previewTone, compact = false, disabled, onChange, onRemove, }: BrandingAssetFieldProps) { const previewUrl = pendingUrl || (hasCustomAsset ? currentUrl : ""); const previewClassName = { light: "bg-white", dark: "bg-neutral-950", neutral: "bg-muted/40", }[previewTone]; return (
{description ?

{description}

: null}
onChange(asset, event)} className="cursor-pointer" /> {hasCustomAsset ? ( ) : null}
{previewUrl ? ( {`${title} ) : (
)}
); }