refactor: replace react-hot-toast with custom toast component, introduce SubmitButton for forms, and migrate status messages to toasts

This commit is contained in:
poyrazavsever
2026-06-09 00:18:46 +03:00
parent b838c94be2
commit 6b48da58bf
12 changed files with 3452 additions and 882 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ import { DefaultChatTransport, type UIMessage } from "ai";
import { Brain, Loader2, MessageSquare, Plus, Send, Trash2 } from "lucide-react";
import { Button } from "poyraz-ui/atoms";
import { useEffect, useRef, useState } from "react";
import toast from "react-hot-toast";
import { toast } from "poyraz-ui/molecules";
type ChatSession = {
id: string;
@@ -7,7 +7,7 @@ import { Card, CardContent, Badge, Button, Input, Textarea, Label } from "poyraz
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DialogDescription } from "poyraz-ui/molecules";
import { Phone, Mail, ExternalLink, Calendar, Plus, MessageSquare, Briefcase, FileText, UserPlus, Loader2 } from "lucide-react";
import Link from "next/link";
import toast from "react-hot-toast";
import { toast } from "poyraz-ui/molecules";
import { addClientActivity } from "./actions";
export type ClientDetailData = {
+11 -18
View File
@@ -5,6 +5,7 @@ import { AlertTriangle, Blocks, Brain, Key, Save, Shield, User } from "lucide-re
import { updatePassword, updateProfile } from "./actions";
import { createClient } from "@/lib/supabase/client";
import { Button, Card, CardContent, Input, Label } from "poyraz-ui/atoms";
import { toast } from "poyraz-ui/molecules";
type AiProvider = "groq" | "ollama" | "openai" | "gemini";
@@ -15,16 +16,13 @@ export default function SettingsPage() {
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [avatarUrl, setAvatarUrl] = useState("");
const [profileSaveStatus, setProfileSaveStatus] = useState("");
// Security States
const [passwordSaveStatus, setPasswordSaveStatus] = useState("");
const formRef = useRef<HTMLFormElement>(null);
// AI States
const [aiProvider, setAiProvider] = useState<AiProvider>("gemini");
const [apiKey, setApiKey] = useState("");
const [aiSaveStatus, setAiSaveStatus] = useState("");
// Supabase
const [supabase] = useState(() => createClient());
@@ -63,11 +61,11 @@ export default function SettingsPage() {
.single();
if (settings && isActive) {
setAiProvider((settings.ai_model as AiProvider) || "gemini");
setAiProvider((settings.ai_provider as AiProvider) || "gemini");
setApiKey(settings.api_key || "");
// Also sync to local storage for existing API route calls if they use it
localStorage.setItem("mindspace_ai_provider", settings.ai_model);
localStorage.setItem("mindspace_ai_provider", settings.ai_provider || "gemini");
localStorage.setItem("mindspace_api_key", settings.api_key || "");
}
};
@@ -79,24 +77,22 @@ export default function SettingsPage() {
const handleProfileAction = async (formData: FormData) => {
const response = await updateProfile(formData);
if (response?.error) {
setProfileSaveStatus(`Hata: ${response.error}`);
toast.error(`Hata: ${response.error}`);
} else {
setProfileSaveStatus("Profil güncellendi!");
toast.success("Profil güncellendi!");
const avatar = formData.get("avatar");
if (avatar instanceof File && avatar.size > 0) window.location.reload();
}
setTimeout(() => setProfileSaveStatus(""), 3000);
};
const handlePasswordAction = async (formData: FormData) => {
const response = await updatePassword(formData);
if (response?.error) {
setPasswordSaveStatus(`Hata: ${response.error}`);
toast.error(`Hata: ${response.error}`);
} else {
setPasswordSaveStatus("Şifre güncellendi!");
toast.success("Şifre güncellendi!");
formRef.current?.reset();
}
setTimeout(() => setPasswordSaveStatus(""), 3000);
};
const handleSaveAI = async () => {
@@ -109,7 +105,8 @@ export default function SettingsPage() {
.from("app_settings")
.upsert({
user_id: user.id,
ai_model: aiProvider,
ai_provider: aiProvider,
ai_model: null, // Reset to allow default model fallback
api_key: apiKey,
updated_at: new Date().toISOString()
}, { onConflict: 'user_id' });
@@ -120,12 +117,11 @@ export default function SettingsPage() {
localStorage.setItem("mindspace_ai_provider", aiProvider);
localStorage.setItem("mindspace_api_key", apiKey);
setAiSaveStatus("Yapay Zeka ayarları kaydedildi!");
toast.success("Yapay Zeka ayarları kaydedildi!");
} catch (e: any) {
console.error(e);
setAiSaveStatus("Hata oluştu, veritabanına kaydedilemedi.");
toast.error("Hata oluştu, veritabanına kaydedilemedi.");
}
setTimeout(() => setAiSaveStatus(""), 3000);
};
return (
@@ -204,7 +200,6 @@ export default function SettingsPage() {
<Button type="submit" className="gap-2">
<Save className="h-4 w-4" /> Profili Kaydet
</Button>
{profileSaveStatus && <span className="text-sm font-medium text-emerald-500">{profileSaveStatus}</span>}
</div>
</form>
</CardContent>
@@ -224,7 +219,6 @@ export default function SettingsPage() {
<Button type="submit" className="gap-2">
<Save className="h-4 w-4" /> Şifreyi Güncelle
</Button>
{passwordSaveStatus && <span className="text-sm font-medium text-emerald-500">{passwordSaveStatus}</span>}
</div>
</form>
</CardContent>
@@ -285,7 +279,6 @@ export default function SettingsPage() {
<Button onClick={handleSaveAI} className="gap-2">
<Save className="h-4 w-4" /> Ayarları Kaydet
</Button>
{aiSaveStatus && <span className="text-sm font-medium text-emerald-500">{aiSaveStatus}</span>}
</div>
</div>
</CardContent>
+2 -2
View File
@@ -2,7 +2,7 @@ import type { Metadata, Viewport } from "next";
import "./globals.css";
import { Geist } from "next/font/google";
import { cn } from "@/lib/utils";
import { Toaster } from "react-hot-toast";
import { Toaster } from "poyraz-ui/molecules";
import { OfflineIndicator } from "@/components/ui/offline-indicator";
const geist = Geist({ subsets: ["latin"], variable: "--font-sans" });
@@ -36,7 +36,7 @@ export default function RootLayout({
<body>
{children}
<OfflineIndicator />
<Toaster position="top-right" />
<Toaster />
</body>
</html>
);
+3 -2
View File
@@ -5,6 +5,7 @@ import { getFirstAdminSetupState } from "@/lib/auth/first-admin-setup";
import { LockKeyhole, LogIn, Mail } from "lucide-react";
import Link from "next/link";
import { Button, Input, Label } from "poyraz-ui/atoms";
import { SubmitButton } from "@/components/auth/submit-button";
export default async function LoginPage({
searchParams,
@@ -65,10 +66,10 @@ export default async function LoginPage({
</div>
</div>
<Button formAction={login} className="h-11 w-full gap-2">
<SubmitButton formAction={login} className="h-11 w-full gap-2" pendingText="Giriş yapılıyor...">
<LogIn className="h-4 w-4" />
Giriş yap
</Button>
</SubmitButton>
</form>
}
secondaryAction={null}
@@ -6,7 +6,7 @@ import { tr } from "date-fns/locale";
import { Card, CardContent, Badge, Button, Textarea, Label } from "poyraz-ui/atoms";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from "poyraz-ui/molecules";
import { CheckCircle2, Clock, MessageSquare, Loader2, RefreshCw } from "lucide-react";
import toast from "react-hot-toast";
import { toast } from "poyraz-ui/molecules";
import { createRevisionRequest } from "./actions";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "poyraz-ui/molecules";
+3 -2
View File
@@ -6,6 +6,7 @@ import { LockKeyhole, Mail, UserPlus } from "lucide-react";
import Link from "next/link";
import { redirect } from "next/navigation";
import { Button, Input, Label } from "poyraz-ui/atoms";
import { SubmitButton } from "@/components/auth/submit-button";
export default async function RegisterPage({
searchParams,
@@ -69,10 +70,10 @@ export default async function RegisterPage({
</div>
</div>
<Button formAction={signup} className="h-11 w-full gap-2">
<SubmitButton formAction={signup} className="h-11 w-full gap-2" pendingText="Oluşturuluyor...">
<UserPlus className="h-4 w-4" />
Admin hesabını oluştur
</Button>
</SubmitButton>
</form>
}
secondaryAction={null}