refactor: replace react-hot-toast with custom toast component, introduce SubmitButton for forms, and migrate status messages to toasts
This commit is contained in:
@@ -6,7 +6,7 @@ import { DefaultChatTransport, type UIMessage } from "ai";
|
|||||||
import { Brain, Loader2, MessageSquare, Plus, Send, Trash2 } from "lucide-react";
|
import { Brain, Loader2, MessageSquare, Plus, Send, Trash2 } from "lucide-react";
|
||||||
import { Button } from "poyraz-ui/atoms";
|
import { Button } from "poyraz-ui/atoms";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import { toast } from "poyraz-ui/molecules";
|
||||||
|
|
||||||
type ChatSession = {
|
type ChatSession = {
|
||||||
id: string;
|
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 { 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 { Phone, Mail, ExternalLink, Calendar, Plus, MessageSquare, Briefcase, FileText, UserPlus, Loader2 } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import toast from "react-hot-toast";
|
import { toast } from "poyraz-ui/molecules";
|
||||||
import { addClientActivity } from "./actions";
|
import { addClientActivity } from "./actions";
|
||||||
|
|
||||||
export type ClientDetailData = {
|
export type ClientDetailData = {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { AlertTriangle, Blocks, Brain, Key, Save, Shield, User } from "lucide-re
|
|||||||
import { updatePassword, updateProfile } from "./actions";
|
import { updatePassword, updateProfile } from "./actions";
|
||||||
import { createClient } from "@/lib/supabase/client";
|
import { createClient } from "@/lib/supabase/client";
|
||||||
import { Button, Card, CardContent, Input, Label } from "poyraz-ui/atoms";
|
import { Button, Card, CardContent, Input, Label } from "poyraz-ui/atoms";
|
||||||
|
import { toast } from "poyraz-ui/molecules";
|
||||||
|
|
||||||
type AiProvider = "groq" | "ollama" | "openai" | "gemini";
|
type AiProvider = "groq" | "ollama" | "openai" | "gemini";
|
||||||
|
|
||||||
@@ -15,16 +16,13 @@ export default function SettingsPage() {
|
|||||||
const [firstName, setFirstName] = useState("");
|
const [firstName, setFirstName] = useState("");
|
||||||
const [lastName, setLastName] = useState("");
|
const [lastName, setLastName] = useState("");
|
||||||
const [avatarUrl, setAvatarUrl] = useState("");
|
const [avatarUrl, setAvatarUrl] = useState("");
|
||||||
const [profileSaveStatus, setProfileSaveStatus] = useState("");
|
|
||||||
|
|
||||||
// Security States
|
// Security States
|
||||||
const [passwordSaveStatus, setPasswordSaveStatus] = useState("");
|
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
|
|
||||||
// AI States
|
// AI States
|
||||||
const [aiProvider, setAiProvider] = useState<AiProvider>("gemini");
|
const [aiProvider, setAiProvider] = useState<AiProvider>("gemini");
|
||||||
const [apiKey, setApiKey] = useState("");
|
const [apiKey, setApiKey] = useState("");
|
||||||
const [aiSaveStatus, setAiSaveStatus] = useState("");
|
|
||||||
|
|
||||||
// Supabase
|
// Supabase
|
||||||
const [supabase] = useState(() => createClient());
|
const [supabase] = useState(() => createClient());
|
||||||
@@ -63,11 +61,11 @@ export default function SettingsPage() {
|
|||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (settings && isActive) {
|
if (settings && isActive) {
|
||||||
setAiProvider((settings.ai_model as AiProvider) || "gemini");
|
setAiProvider((settings.ai_provider as AiProvider) || "gemini");
|
||||||
setApiKey(settings.api_key || "");
|
setApiKey(settings.api_key || "");
|
||||||
|
|
||||||
// Also sync to local storage for existing API route calls if they use it
|
// 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 || "");
|
localStorage.setItem("mindspace_api_key", settings.api_key || "");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -79,24 +77,22 @@ export default function SettingsPage() {
|
|||||||
const handleProfileAction = async (formData: FormData) => {
|
const handleProfileAction = async (formData: FormData) => {
|
||||||
const response = await updateProfile(formData);
|
const response = await updateProfile(formData);
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
setProfileSaveStatus(`Hata: ${response.error}`);
|
toast.error(`Hata: ${response.error}`);
|
||||||
} else {
|
} else {
|
||||||
setProfileSaveStatus("Profil güncellendi!");
|
toast.success("Profil güncellendi!");
|
||||||
const avatar = formData.get("avatar");
|
const avatar = formData.get("avatar");
|
||||||
if (avatar instanceof File && avatar.size > 0) window.location.reload();
|
if (avatar instanceof File && avatar.size > 0) window.location.reload();
|
||||||
}
|
}
|
||||||
setTimeout(() => setProfileSaveStatus(""), 3000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePasswordAction = async (formData: FormData) => {
|
const handlePasswordAction = async (formData: FormData) => {
|
||||||
const response = await updatePassword(formData);
|
const response = await updatePassword(formData);
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
setPasswordSaveStatus(`Hata: ${response.error}`);
|
toast.error(`Hata: ${response.error}`);
|
||||||
} else {
|
} else {
|
||||||
setPasswordSaveStatus("Şifre güncellendi!");
|
toast.success("Şifre güncellendi!");
|
||||||
formRef.current?.reset();
|
formRef.current?.reset();
|
||||||
}
|
}
|
||||||
setTimeout(() => setPasswordSaveStatus(""), 3000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveAI = async () => {
|
const handleSaveAI = async () => {
|
||||||
@@ -109,7 +105,8 @@ export default function SettingsPage() {
|
|||||||
.from("app_settings")
|
.from("app_settings")
|
||||||
.upsert({
|
.upsert({
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
ai_model: aiProvider,
|
ai_provider: aiProvider,
|
||||||
|
ai_model: null, // Reset to allow default model fallback
|
||||||
api_key: apiKey,
|
api_key: apiKey,
|
||||||
updated_at: new Date().toISOString()
|
updated_at: new Date().toISOString()
|
||||||
}, { onConflict: 'user_id' });
|
}, { onConflict: 'user_id' });
|
||||||
@@ -120,12 +117,11 @@ export default function SettingsPage() {
|
|||||||
localStorage.setItem("mindspace_ai_provider", aiProvider);
|
localStorage.setItem("mindspace_ai_provider", aiProvider);
|
||||||
localStorage.setItem("mindspace_api_key", apiKey);
|
localStorage.setItem("mindspace_api_key", apiKey);
|
||||||
|
|
||||||
setAiSaveStatus("Yapay Zeka ayarları kaydedildi!");
|
toast.success("Yapay Zeka ayarları kaydedildi!");
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
setAiSaveStatus("Hata oluştu, veritabanına kaydedilemedi.");
|
toast.error("Hata oluştu, veritabanına kaydedilemedi.");
|
||||||
}
|
}
|
||||||
setTimeout(() => setAiSaveStatus(""), 3000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -204,7 +200,6 @@ export default function SettingsPage() {
|
|||||||
<Button type="submit" className="gap-2">
|
<Button type="submit" className="gap-2">
|
||||||
<Save className="h-4 w-4" /> Profili Kaydet
|
<Save className="h-4 w-4" /> Profili Kaydet
|
||||||
</Button>
|
</Button>
|
||||||
{profileSaveStatus && <span className="text-sm font-medium text-emerald-500">{profileSaveStatus}</span>}
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -224,7 +219,6 @@ export default function SettingsPage() {
|
|||||||
<Button type="submit" className="gap-2">
|
<Button type="submit" className="gap-2">
|
||||||
<Save className="h-4 w-4" /> Şifreyi Güncelle
|
<Save className="h-4 w-4" /> Şifreyi Güncelle
|
||||||
</Button>
|
</Button>
|
||||||
{passwordSaveStatus && <span className="text-sm font-medium text-emerald-500">{passwordSaveStatus}</span>}
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -285,7 +279,6 @@ export default function SettingsPage() {
|
|||||||
<Button onClick={handleSaveAI} className="gap-2">
|
<Button onClick={handleSaveAI} className="gap-2">
|
||||||
<Save className="h-4 w-4" /> Ayarları Kaydet
|
<Save className="h-4 w-4" /> Ayarları Kaydet
|
||||||
</Button>
|
</Button>
|
||||||
{aiSaveStatus && <span className="text-sm font-medium text-emerald-500">{aiSaveStatus}</span>}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@ import type { Metadata, Viewport } from "next";
|
|||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { Geist } from "next/font/google";
|
import { Geist } from "next/font/google";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Toaster } from "react-hot-toast";
|
import { Toaster } from "poyraz-ui/molecules";
|
||||||
import { OfflineIndicator } from "@/components/ui/offline-indicator";
|
import { OfflineIndicator } from "@/components/ui/offline-indicator";
|
||||||
|
|
||||||
const geist = Geist({ subsets: ["latin"], variable: "--font-sans" });
|
const geist = Geist({ subsets: ["latin"], variable: "--font-sans" });
|
||||||
@@ -36,7 +36,7 @@ export default function RootLayout({
|
|||||||
<body>
|
<body>
|
||||||
{children}
|
{children}
|
||||||
<OfflineIndicator />
|
<OfflineIndicator />
|
||||||
<Toaster position="top-right" />
|
<Toaster />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-2
@@ -5,6 +5,7 @@ import { getFirstAdminSetupState } from "@/lib/auth/first-admin-setup";
|
|||||||
import { LockKeyhole, LogIn, Mail } from "lucide-react";
|
import { LockKeyhole, LogIn, Mail } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Button, Input, Label } from "poyraz-ui/atoms";
|
import { Button, Input, Label } from "poyraz-ui/atoms";
|
||||||
|
import { SubmitButton } from "@/components/auth/submit-button";
|
||||||
|
|
||||||
export default async function LoginPage({
|
export default async function LoginPage({
|
||||||
searchParams,
|
searchParams,
|
||||||
@@ -65,10 +66,10 @@ export default async function LoginPage({
|
|||||||
</div>
|
</div>
|
||||||
</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" />
|
<LogIn className="h-4 w-4" />
|
||||||
Giriş yap
|
Giriş yap
|
||||||
</Button>
|
</SubmitButton>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
secondaryAction={null}
|
secondaryAction={null}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { tr } from "date-fns/locale";
|
|||||||
import { Card, CardContent, Badge, Button, Textarea, Label } from "poyraz-ui/atoms";
|
import { Card, CardContent, Badge, Button, Textarea, Label } from "poyraz-ui/atoms";
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from "poyraz-ui/molecules";
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from "poyraz-ui/molecules";
|
||||||
import { CheckCircle2, Clock, MessageSquare, Loader2, RefreshCw } from "lucide-react";
|
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 { createRevisionRequest } from "./actions";
|
||||||
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "poyraz-ui/molecules";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "poyraz-ui/molecules";
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { LockKeyhole, Mail, UserPlus } from "lucide-react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { Button, Input, Label } from "poyraz-ui/atoms";
|
import { Button, Input, Label } from "poyraz-ui/atoms";
|
||||||
|
import { SubmitButton } from "@/components/auth/submit-button";
|
||||||
|
|
||||||
export default async function RegisterPage({
|
export default async function RegisterPage({
|
||||||
searchParams,
|
searchParams,
|
||||||
@@ -69,10 +70,10 @@ export default async function RegisterPage({
|
|||||||
</div>
|
</div>
|
||||||
</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" />
|
<UserPlus className="h-4 w-4" />
|
||||||
Admin hesabını oluştur
|
Admin hesabını oluştur
|
||||||
</Button>
|
</SubmitButton>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
secondaryAction={null}
|
secondaryAction={null}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ export function AuthPageShell({
|
|||||||
<div className="w-full relative z-10 p-10 text-sm text-primary-foreground/78">
|
<div className="w-full relative z-10 p-10 text-sm text-primary-foreground/78">
|
||||||
<span>Açık kaynak ve self-host edilebilir.</span>{" "}
|
<span>Açık kaynak ve self-host edilebilir.</span>{" "}
|
||||||
<Link
|
<Link
|
||||||
href="https://github.com/poyrazavsever/cognis"
|
href="https://github.com/poyrazavsever/revanios"
|
||||||
className="font-semibold text-primary-foreground underline-offset-4 hover:underline"
|
className="font-semibold text-primary-foreground underline-offset-4 hover:underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useFormStatus } from "react-dom";
|
||||||
|
import { Button } from "poyraz-ui/atoms";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface SubmitButtonProps extends React.ComponentProps<typeof Button> {
|
||||||
|
pendingText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SubmitButton({
|
||||||
|
children,
|
||||||
|
pendingText,
|
||||||
|
...props
|
||||||
|
}: SubmitButtonProps) {
|
||||||
|
const { pending } = useFormStatus();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button disabled={pending} {...props}>
|
||||||
|
{pending ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="h-4 w-4 animate-spin" />
|
||||||
|
{pendingText || children}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { toast } from 'react-hot-toast'
|
import { toast } from 'poyraz-ui/molecules'
|
||||||
|
|
||||||
export function ErrorToaster({ message }: { message: string }) {
|
export function ErrorToaster({ message }: { message: string }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Generated
+3397
-852
File diff suppressed because it is too large
Load Diff
@@ -45,7 +45,6 @@
|
|||||||
"react": "^19.2.7",
|
"react": "^19.2.7",
|
||||||
"react-dom": "^19.2.7",
|
"react-dom": "^19.2.7",
|
||||||
"react-hook-form": "^7.77.0",
|
"react-hook-form": "^7.77.0",
|
||||||
"react-hot-toast": "^2.6.0",
|
|
||||||
"recharts": "^2.15.4",
|
"recharts": "^2.15.4",
|
||||||
"shadcn": "^4.10.0",
|
"shadcn": "^4.10.0",
|
||||||
"tailwind-merge": "^3.6.0",
|
"tailwind-merge": "^3.6.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user