hata ayıklama ve kod temizliği

This commit is contained in:
2026-01-11 23:58:09 +03:00
parent b2a915240f
commit 32009b4886
22 changed files with 117 additions and 92 deletions

6
.eslintrc.json Normal file
View File

@@ -0,0 +1,6 @@
{
"extends": [
"next/core-web-vitals",
"next/typescript"
]
}

View File

@@ -1,6 +1,6 @@
import { createClient } from "@/lib/supabase-server" import { createClient } from "@/lib/supabase-server"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { DollarSign, ShoppingCart, Users, CreditCard, Package } from "lucide-react" import { DollarSign, ShoppingCart, Users, Package } from "lucide-react"
import Link from "next/link" import Link from "next/link"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"

View File

@@ -3,7 +3,15 @@
import { createClient } from "@/lib/supabase-server" import { createClient } from "@/lib/supabase-server"
import { revalidatePath } from "next/cache" import { revalidatePath } from "next/cache"
export async function createProduct(data: any) { interface ProductData {
name: string
category: string
description?: string
price: number
image_url?: string
}
export async function createProduct(data: ProductData) {
const supabase = createClient() const supabase = createClient()
// Validate data manually or use Zod schema here again securely // Validate data manually or use Zod schema here again securely
@@ -23,12 +31,12 @@ export async function createProduct(data: any) {
revalidatePath("/dashboard/products") revalidatePath("/dashboard/products")
return { success: true } return { success: true }
} catch (error: any) { } catch (error) {
return { success: false, error: error.message } return { success: false, error: (error as Error).message }
} }
} }
export async function updateProduct(id: number, data: any) { export async function updateProduct(id: number, data: ProductData) {
const supabase = createClient() const supabase = createClient()
try { try {
@@ -45,7 +53,7 @@ export async function updateProduct(id: number, data: any) {
revalidatePath("/dashboard/products") revalidatePath("/dashboard/products")
revalidatePath(`/dashboard/products/${id}`) revalidatePath(`/dashboard/products/${id}`)
return { success: true } return { success: true }
} catch (error: any) { } catch (error) {
return { success: false, error: error.message } return { success: false, error: (error as Error).message }
} }
} }

View File

@@ -2,7 +2,7 @@ import { createClient } from "@/lib/supabase-server"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { UserForm } from "@/components/dashboard/user-form" import { UserForm } from "@/components/dashboard/user-form"
import { notFound } from "next/navigation"
import { getProfile } from "@/lib/data" import { getProfile } from "@/lib/data"
export default async function ProfilePage() { export default async function ProfilePage() {
@@ -23,9 +23,17 @@ export default async function ProfilePage() {
return <div>Profil verisi bulunamadı.</div> return <div>Profil verisi bulunamadı.</div>
} }
const parts = (profile.full_name || "").split(' ') // Improved name parsing logic
const firstName = parts[0] || "" const fullName = (profile.full_name || "").trim()
const lastName = parts.slice(1).join(' ') || "" const firstSpaceIndex = fullName.indexOf(' ')
let firstName = fullName
let lastName = ""
if (firstSpaceIndex > 0) {
firstName = fullName.substring(0, firstSpaceIndex)
lastName = fullName.substring(firstSpaceIndex + 1)
}
const initialData = { const initialData = {
firstName, firstName,

View File

@@ -2,8 +2,7 @@ import { createClient } from "@/lib/supabase-server"
import { SiteSettingsForm } from "@/components/dashboard/site-settings-form" import { SiteSettingsForm } from "@/components/dashboard/site-settings-form"
import { AppearanceForm } from "@/components/dashboard/appearance-form" import { AppearanceForm } from "@/components/dashboard/appearance-form"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
export default async function SettingsPage() { export default async function SettingsPage() {

View File

@@ -3,7 +3,6 @@
import { createClient } from "@/lib/supabase-server" import { createClient } from "@/lib/supabase-server"
import { createClient as createSupabaseClient } from "@supabase/supabase-js" import { createClient as createSupabaseClient } from "@supabase/supabase-js"
import { revalidatePath } from "next/cache" import { revalidatePath } from "next/cache"
import { redirect } from "next/navigation"
// WARNING: specialized client for admin actions only // WARNING: specialized client for admin actions only
// This requires SUPABASE_SERVICE_ROLE_KEY to be set in .env.local // This requires SUPABASE_SERVICE_ROLE_KEY to be set in .env.local
@@ -19,20 +18,12 @@ const supabaseAdmin = createSupabaseClient(
) )
export async function createUser(firstName: string, lastName: string, email: string, password: string, role: 'admin' | 'user', phone?: string) { export async function createUser(firstName: string, lastName: string, email: string, password: string, role: 'admin' | 'user', phone?: string) {
const supabase = createClient()
// 1. Check if current user is admin // 1. Check if current user is admin
const { data: { user: currentUser } } = await supabase.auth.getUser() try {
if (!currentUser) return { error: "Oturum açmanız gerekiyor." } await assertAdmin()
} catch (error) {
const { data: profile } = await supabase return { error: (error as Error).message }
.from('profiles')
.select('role')
.eq('id', currentUser.id)
.single()
if (!profile || profile.role !== 'admin') {
return { error: "Yetkisiz işlem. Sadece yöneticiler kullanıcı oluşturabilir." }
} }
// 2. Create user using Admin client // 2. Create user using Admin client
@@ -74,14 +65,13 @@ export async function createUser(firstName: string, lastName: string, email: str
} }
export async function deleteUser(userId: string) { export async function deleteUser(userId: string) {
const supabase = createClient()
// Check admin // Check admin
const { data: { user: currentUser } } = await supabase.auth.getUser() try {
if (!currentUser) return { error: "Oturum açmanız gerekiyor." } await assertAdmin()
} catch (error: any) {
const { data: profile } = await supabase.from('profiles').select('role').eq('id', currentUser.id).single() return { error: error.message }
if (profile?.role !== 'admin') return { error: "Yetkisiz işlem." } }
// Delete user // Delete user
const { error } = await supabaseAdmin.auth.admin.deleteUser(userId) const { error } = await supabaseAdmin.auth.admin.deleteUser(userId)
@@ -93,15 +83,13 @@ export async function deleteUser(userId: string) {
} }
export async function updateUser(userId: string, data: { firstName: string, lastName: string, email: string, password?: string, role: 'admin' | 'user', phone?: string }) { export async function updateUser(userId: string, data: { firstName: string, lastName: string, email: string, password?: string, role: 'admin' | 'user', phone?: string }) {
const supabase = createClient()
// Check admin // Check admin
const { data: { user: currentUser } } = await supabase.auth.getUser() try {
if (!currentUser) return { error: "Oturum açmanız gerekiyor." } await assertAdmin()
} catch (error: any) {
// Check if current user is admin return { error: error.message }
const { data: profile } = await supabase.from('profiles').select('role').eq('id', currentUser.id).single() }
if (profile?.role !== 'admin') return { error: "Yetkisiz işlem." }
// 1. Update Profile (Role and Name) // 1. Update Profile (Role and Name)
const { error: profileError } = await supabaseAdmin const { error: profileError } = await supabaseAdmin
@@ -116,7 +104,7 @@ export async function updateUser(userId: string, data: { firstName: string, last
if (profileError) return { error: "Profil güncellenemedi: " + profileError.message } if (profileError) return { error: "Profil güncellenemedi: " + profileError.message }
// 2. Update Auth (Email and Password) // 2. Update Auth (Email and Password)
const authUpdates: any = { const authUpdates: { email: string; user_metadata: { full_name: string }; password?: string } = {
email: data.email, email: data.email,
user_metadata: { user_metadata: {
full_name: `${data.firstName} ${data.lastName}`.trim() full_name: `${data.firstName} ${data.lastName}`.trim()
@@ -162,3 +150,21 @@ export async function updateProfile(data: { firstName: string, lastName: string,
revalidatePath("/dashboard/profile") revalidatePath("/dashboard/profile")
return { success: true } return { success: true }
} }
async function assertAdmin() {
const supabase = createClient()
const { data: { user: currentUser } } = await supabase.auth.getUser()
if (!currentUser) throw new Error("Oturum açmanız gerekiyor.")
const { data: profile } = await supabase
.from('profiles')
.select('role')
.eq('id', currentUser.id)
.single()
if (!profile || profile.role !== 'admin') {
throw new Error("Yetkisiz işlem. Sadece yöneticiler bu işlemi gerçekleştirebilir.")
}
return currentUser
}

View File

@@ -39,7 +39,7 @@ export default function ContactPage() {
} else { } else {
toast.error("Hata: " + response.error) toast.error("Hata: " + response.error)
} }
} catch (error) { } catch {
toast.error("Bir hata oluştu.") toast.error("Bir hata oluştu.")
} finally { } finally {
setIsSubmitting(false) setIsSubmitting(false)

View File

@@ -1,5 +1,5 @@
import Image from "next/image"
export default function CorporatePage() { export default function CorporatePage() {
return ( return (
@@ -8,7 +8,7 @@ export default function CorporatePage() {
<section className="space-y-6 text-center"> <section className="space-y-6 text-center">
<h1 className="text-4xl md:text-5xl font-bold tracking-tight font-outfit">Hakkımızda</h1> <h1 className="text-4xl md:text-5xl font-bold tracking-tight font-outfit">Hakkımızda</h1>
<p className="text-xl text-muted-foreground"> <p className="text-xl text-muted-foreground">
1995'ten beri güvenliğinizi en değerli hazinemiz olarak görüyoruz. 1995&apos;ten beri güvenliğinizi en değerli hazinemiz olarak görüyoruz.
</p> </p>
</section> </section>
@@ -28,7 +28,7 @@ export default function CorporatePage() {
</p> </p>
<h2 className="text-3xl font-bold pt-4">Vizyonumuz</h2> <h2 className="text-3xl font-bold pt-4">Vizyonumuz</h2>
<p className="text-slate-600 dark:text-slate-300 leading-relaxed"> <p className="text-slate-600 dark:text-slate-300 leading-relaxed">
Türkiye'nin lider çelik kasa üreticisi olarak, global pazarda güvenilirlik ve kalite Türkiye&apos;nin lider çelik kasa üreticisi olarak, global pazarda güvenilirlik ve kalite
denince akla gelen ilk marka olmak. Yenilikçi Ar-Ge çalışmalarımızla güvenlik teknolojilerine denince akla gelen ilk marka olmak. Yenilikçi Ar-Ge çalışmalarımızla güvenlik teknolojilerine
yön vermek. yön vermek.
</p> </p>

View File

@@ -36,7 +36,7 @@ export default function LoginPage() {
router.push("/dashboard") router.push("/dashboard")
router.refresh() router.refresh()
} catch (err: any) { } catch {
setError("Bir hata oluştu. Lütfen tekrar deneyin.") setError("Bir hata oluştu. Lütfen tekrar deneyin.")
} finally { } finally {
setLoading(false) setLoading(false)

View File

@@ -1,6 +1,6 @@
import Link from "next/link" import Link from "next/link"
import Image from "next/image" import Image from "next/image"
import { ArrowRight, ShieldCheck, Lock, Award, History, LayoutDashboard, LogIn } from "lucide-react" import { ArrowRight, ShieldCheck, Lock, History, LayoutDashboard } from "lucide-react"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card" import { Card, CardContent } from "@/components/ui/card"
import { createClient } from "@/lib/supabase-server" import { createClient } from "@/lib/supabase-server"

View File

@@ -1,7 +1,7 @@
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import Image from "next/image"
const products = [ const products = [
{ {

View File

@@ -1,7 +1,7 @@
"use client" "use client"
import { useState } from "react" import { useState } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link" import Link from "next/link"
import { supabase } from "@/lib/supabase" import { supabase } from "@/lib/supabase"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
@@ -11,7 +11,6 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }
import { AlertCircle, Loader2, CheckCircle2 } from "lucide-react" import { AlertCircle, Loader2, CheckCircle2 } from "lucide-react"
export default function SignUpPage() { export default function SignUpPage() {
const router = useRouter()
const [email, setEmail] = useState("") const [email, setEmail] = useState("")
const [password, setPassword] = useState("") const [password, setPassword] = useState("")
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
@@ -76,7 +75,7 @@ export default function SignUpPage() {
<CardHeader className="space-y-1"> <CardHeader className="space-y-1">
<CardTitle className="text-2xl font-bold">Hesap Oluştur</CardTitle> <CardTitle className="text-2xl font-bold">Hesap Oluştur</CardTitle>
<CardDescription> <CardDescription>
ParaKasa'ya katılmak için bilgilerinizi girin ParaKasa&apos;ya katılmak için bilgilerinizi girin
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>

View File

@@ -1,4 +1,4 @@
import type { Metadata } from "next";
import { Inter, Outfit } from "next/font/google"; import { Inter, Outfit } from "next/font/google";
import { Toaster } from "sonner"; import { Toaster } from "sonner";
import "./globals.css"; import "./globals.css";

View File

@@ -62,8 +62,8 @@ export function CategoryForm({ initialData }: CategoryFormProps) {
try { try {
if (initialData) { if (initialData) {
const result = await updateCategory(initialData.id, data) const result = await updateCategory(initialData.id, data)
if ((result as any).error) { if ('error' in result) {
toast.error((result as any).error) toast.error(result.error)
} else { } else {
toast.success(toastMessage) toast.success(toastMessage)
router.push(`/dashboard/categories`) router.push(`/dashboard/categories`)
@@ -71,15 +71,15 @@ export function CategoryForm({ initialData }: CategoryFormProps) {
} }
} else { } else {
const result = await createCategory(data) const result = await createCategory(data)
if ((result as any).error) { if ('error' in result) {
toast.error((result as any).error) toast.error(result.error)
} else { } else {
toast.success(toastMessage) toast.success(toastMessage)
router.push(`/dashboard/categories`) router.push(`/dashboard/categories`)
router.refresh() router.refresh()
} }
} }
} catch (error) { } catch {
toast.error("Bir hata oluştu.") toast.error("Bir hata oluştu.")
} finally { } finally {
setLoading(false) setLoading(false)
@@ -90,14 +90,14 @@ export function CategoryForm({ initialData }: CategoryFormProps) {
setLoading(true) setLoading(true)
try { try {
const result = await deleteCategory(initialData!.id) const result = await deleteCategory(initialData!.id)
if ((result as any).error) { if ('error' in result) {
toast.error((result as any).error) toast.error(result.error)
} else { } else {
toast.success("Kategori silindi.") toast.success("Kategori silindi.")
router.push(`/dashboard/categories`) router.push(`/dashboard/categories`)
router.refresh() router.refresh()
} }
} catch (error) { } catch {
toast.error("Silme işlemi başarısız.") toast.error("Silme işlemi başarısız.")
} finally { } finally {
setLoading(false) setLoading(false)

View File

@@ -57,7 +57,7 @@ export function PasswordForm() {
toast.success("Şifreniz başarıyla güncellendi.") toast.success("Şifreniz başarıyla güncellendi.")
form.reset() form.reset()
router.refresh() router.refresh()
} catch (error) { } catch {
toast.error("Bir sorun oluştu.") toast.error("Bir sorun oluştu.")
} finally { } finally {
setLoading(false) setLoading(false)

View File

@@ -94,7 +94,7 @@ export function ProductForm({ initialData }: ProductFormProps) {
toast.success(initialData ? "Ürün güncellendi" : "Ürün başarıyla oluşturuldu") toast.success(initialData ? "Ürün güncellendi" : "Ürün başarıyla oluşturuldu")
router.push("/dashboard/products") router.push("/dashboard/products")
router.refresh() router.refresh()
} catch (error) { } catch {
toast.error("Bir aksilik oldu") toast.error("Bir aksilik oldu")
} finally { } finally {
setLoading(false) setLoading(false)

View File

@@ -43,7 +43,7 @@ const sidebarItems = [
}, },
] ]
interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> { } type SidebarProps = React.HTMLAttributes<HTMLDivElement>
export function Sidebar({ className }: SidebarProps) { export function Sidebar({ className }: SidebarProps) {
const pathname = usePathname() const pathname = usePathname()

View File

@@ -54,7 +54,6 @@ export function SiteSettingsForm({ initialData }: SiteSettingsFormProps) {
const onSubmit = async (data: SettingsFormValues) => { const onSubmit = async (data: SettingsFormValues) => {
setLoading(true) setLoading(true)
try { try {
// @ts-ignore
const result = await updateSiteSettings(data) const result = await updateSiteSettings(data)
if (result.error) { if (result.error) {
@@ -64,7 +63,7 @@ export function SiteSettingsForm({ initialData }: SiteSettingsFormProps) {
toast.success("Site ayarları güncellendi.") toast.success("Site ayarları güncellendi.")
router.refresh() router.refresh()
} catch (error) { } catch {
toast.error("Bir sorun oluştu.") toast.error("Bir sorun oluştu.")
} finally { } finally {
setLoading(false) setLoading(false)

View File

@@ -32,23 +32,34 @@ const userSchema = z.object({
firstName: z.string().min(2, "Ad en az 2 karakter olmalıdır."), firstName: z.string().min(2, "Ad en az 2 karakter olmalıdır."),
lastName: z.string().min(2, "Soyad en az 2 karakter olmalıdır."), lastName: z.string().min(2, "Soyad en az 2 karakter olmalıdır."),
email: z.string().email("Geçerli bir e-posta adresi giriniz."), email: z.string().email("Geçerli bir e-posta adresi giriniz."),
password: z.string().optional(), // Password is optional on edit password: z.string().optional(),
confirmPassword: z.string().optional(), confirmPassword: z.string().optional(),
role: z.enum(["admin", "user"]), role: z.enum(["admin", "user"]),
phone: z.string().optional(), phone: z.string().optional(),
}).refine((data) => { }).superRefine((data, ctx) => {
// 1. Password match check // 1. Password match check
if (data.password && data.password !== data.confirmPassword) { if (data.password && data.password !== data.confirmPassword) {
return false; ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Şifreler eşleşmiyor.",
path: ["confirmPassword"],
});
} }
// 2. New user password requirement check (simplified here, but strict would check id)
// We handle the "required" part in onSubmit manually for now as per previous logic, // 2. New user password requirement check
// but the matching check is now here. // If there is no ID (we can't easily check for ID here in schema without context,
return true // but typically empty password on CREATE is invalid unless handled elsewhere.
}, { // However, the component logic implies 'initialData' determines edit/create mode.
message: "Şifreler eşleşmiyor.", // For pure schema validation, we often make password required for create, optional for edit.
path: ["confirmPassword"], // Since we don't pass 'isCreate' to schema, we can enforce minimum length if provided.
}) if (data.password && data.password.length > 0 && data.password.length < 6) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Şifre en az 6 karakter olmalıdır.",
path: ["password"],
});
}
});
type UserFormValues = z.infer<typeof userSchema> type UserFormValues = z.infer<typeof userSchema>
@@ -113,12 +124,8 @@ export function UserForm({ initialData, mode = "admin" }: UserFormProps) {
}) })
} else { } else {
// Admin create mode // Admin create mode
if (!data.password || data.password.length < 6) { // Password requirement is now handled by Zod Schema
toast.error("Yeni kullanıcı için şifre gereklidir (min 6 karakter).") result = await createUser(data.firstName, data.lastName, data.email, data.password!, data.role, data.phone)
setLoading(false)
return
}
result = await createUser(data.firstName, data.lastName, data.email, data.password, data.role, data.phone)
} }
if (result.error) { if (result.error) {
@@ -136,7 +143,7 @@ export function UserForm({ initialData, mode = "admin" }: UserFormProps) {
if (mode === "admin") { if (mode === "admin") {
router.push("/dashboard/users") router.push("/dashboard/users")
} }
} catch (error) { } catch {
toast.error("Bir sorun oluştu.") toast.error("Bir sorun oluştu.")
} finally { } finally {
setLoading(false) setLoading(false)

View File

@@ -18,14 +18,7 @@ import {
} from "@/components/ui/dropdown-menu" } from "@/components/ui/dropdown-menu"
import { createBrowserClient } from "@supabase/ssr" import { createBrowserClient } from "@supabase/ssr"
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { useEffect, useState } from "react"
import { User } from "@supabase/supabase-js"
interface UserProfile {
full_name: string | null
email: string | null
role: string | null
}
interface UserNavProps { interface UserNavProps {
user: { user: {

BIN
lint.log Normal file

Binary file not shown.

View File

@@ -6,7 +6,7 @@
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "eslint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"@hookform/resolvers": "^5.2.2", "@hookform/resolvers": "^5.2.2",