diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..6b10a5b --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": [ + "next/core-web-vitals", + "next/typescript" + ] +} diff --git a/app/(dashboard)/dashboard/page.tsx b/app/(dashboard)/dashboard/page.tsx index e4deb2a..ef7c0f5 100644 --- a/app/(dashboard)/dashboard/page.tsx +++ b/app/(dashboard)/dashboard/page.tsx @@ -1,6 +1,6 @@ import { createClient } from "@/lib/supabase-server" 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 { Button } from "@/components/ui/button" diff --git a/app/(dashboard)/dashboard/products/actions.ts b/app/(dashboard)/dashboard/products/actions.ts index 71acb65..abee491 100644 --- a/app/(dashboard)/dashboard/products/actions.ts +++ b/app/(dashboard)/dashboard/products/actions.ts @@ -3,7 +3,15 @@ import { createClient } from "@/lib/supabase-server" 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() // Validate data manually or use Zod schema here again securely @@ -23,12 +31,12 @@ export async function createProduct(data: any) { revalidatePath("/dashboard/products") return { success: true } - } catch (error: any) { - return { success: false, error: error.message } + } catch (error) { + 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() try { @@ -45,7 +53,7 @@ export async function updateProduct(id: number, data: any) { revalidatePath("/dashboard/products") revalidatePath(`/dashboard/products/${id}`) return { success: true } - } catch (error: any) { - return { success: false, error: error.message } + } catch (error) { + return { success: false, error: (error as Error).message } } } diff --git a/app/(dashboard)/dashboard/profile/page.tsx b/app/(dashboard)/dashboard/profile/page.tsx index 1d4b60a..8bb83b0 100644 --- a/app/(dashboard)/dashboard/profile/page.tsx +++ b/app/(dashboard)/dashboard/profile/page.tsx @@ -2,7 +2,7 @@ import { createClient } from "@/lib/supabase-server" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { UserForm } from "@/components/dashboard/user-form" -import { notFound } from "next/navigation" + import { getProfile } from "@/lib/data" export default async function ProfilePage() { @@ -23,9 +23,17 @@ export default async function ProfilePage() { return
Profil verisi bulunamadı.
} - const parts = (profile.full_name || "").split(' ') - const firstName = parts[0] || "" - const lastName = parts.slice(1).join(' ') || "" + // Improved name parsing logic + const fullName = (profile.full_name || "").trim() + const firstSpaceIndex = fullName.indexOf(' ') + + let firstName = fullName + let lastName = "" + + if (firstSpaceIndex > 0) { + firstName = fullName.substring(0, firstSpaceIndex) + lastName = fullName.substring(firstSpaceIndex + 1) + } const initialData = { firstName, diff --git a/app/(dashboard)/dashboard/settings/page.tsx b/app/(dashboard)/dashboard/settings/page.tsx index 5cced11..23f70ae 100644 --- a/app/(dashboard)/dashboard/settings/page.tsx +++ b/app/(dashboard)/dashboard/settings/page.tsx @@ -2,8 +2,7 @@ import { createClient } from "@/lib/supabase-server" import { SiteSettingsForm } from "@/components/dashboard/site-settings-form" import { AppearanceForm } from "@/components/dashboard/appearance-form" 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" export default async function SettingsPage() { diff --git a/app/(dashboard)/dashboard/users/actions.ts b/app/(dashboard)/dashboard/users/actions.ts index 3655aef..03bafca 100644 --- a/app/(dashboard)/dashboard/users/actions.ts +++ b/app/(dashboard)/dashboard/users/actions.ts @@ -3,7 +3,6 @@ import { createClient } from "@/lib/supabase-server" import { createClient as createSupabaseClient } from "@supabase/supabase-js" import { revalidatePath } from "next/cache" -import { redirect } from "next/navigation" // WARNING: specialized client for admin actions only // 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) { - const supabase = createClient() // 1. Check if current user is admin - const { data: { user: currentUser } } = await supabase.auth.getUser() - if (!currentUser) return { 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') { - return { error: "Yetkisiz işlem. Sadece yöneticiler kullanıcı oluşturabilir." } + try { + await assertAdmin() + } catch (error) { + return { error: (error as Error).message } } // 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) { - const supabase = createClient() // Check admin - const { data: { user: currentUser } } = await supabase.auth.getUser() - if (!currentUser) return { error: "Oturum açmanız gerekiyor." } - - const { data: profile } = await supabase.from('profiles').select('role').eq('id', currentUser.id).single() - if (profile?.role !== 'admin') return { error: "Yetkisiz işlem." } + try { + await assertAdmin() + } catch (error: any) { + return { error: error.message } + } // Delete user 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 }) { - const supabase = createClient() // Check admin - const { data: { user: currentUser } } = await supabase.auth.getUser() - if (!currentUser) return { error: "Oturum açmanız gerekiyor." } - - // Check if current user is admin - const { data: profile } = await supabase.from('profiles').select('role').eq('id', currentUser.id).single() - if (profile?.role !== 'admin') return { error: "Yetkisiz işlem." } + try { + await assertAdmin() + } catch (error: any) { + return { error: error.message } + } // 1. Update Profile (Role and Name) 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 } // 2. Update Auth (Email and Password) - const authUpdates: any = { + const authUpdates: { email: string; user_metadata: { full_name: string }; password?: string } = { email: data.email, user_metadata: { full_name: `${data.firstName} ${data.lastName}`.trim() @@ -162,3 +150,21 @@ export async function updateProfile(data: { firstName: string, lastName: string, revalidatePath("/dashboard/profile") 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 +} diff --git a/app/(public)/contact/page.tsx b/app/(public)/contact/page.tsx index 62155f8..81b1bbd 100644 --- a/app/(public)/contact/page.tsx +++ b/app/(public)/contact/page.tsx @@ -39,7 +39,7 @@ export default function ContactPage() { } else { toast.error("Hata: " + response.error) } - } catch (error) { + } catch { toast.error("Bir hata oluştu.") } finally { setIsSubmitting(false) diff --git a/app/(public)/corporate/page.tsx b/app/(public)/corporate/page.tsx index 419f327..c43b116 100644 --- a/app/(public)/corporate/page.tsx +++ b/app/(public)/corporate/page.tsx @@ -1,5 +1,5 @@ -import Image from "next/image" + export default function CorporatePage() { return ( @@ -8,7 +8,7 @@ export default function CorporatePage() {

Hakkımızda

- 1995'ten beri güvenliğinizi en değerli hazinemiz olarak görüyoruz. + 1995'ten beri güvenliğinizi en değerli hazinemiz olarak görüyoruz.

@@ -28,7 +28,7 @@ export default function CorporatePage() {

Vizyonumuz

- Türkiye'nin lider çelik kasa üreticisi olarak, global pazarda güvenilirlik ve kalite + Türkiye'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 yön vermek.

diff --git a/app/(public)/login/page.tsx b/app/(public)/login/page.tsx index bc053b9..10e6dbf 100644 --- a/app/(public)/login/page.tsx +++ b/app/(public)/login/page.tsx @@ -36,7 +36,7 @@ export default function LoginPage() { router.push("/dashboard") router.refresh() - } catch (err: any) { + } catch { setError("Bir hata oluştu. Lütfen tekrar deneyin.") } finally { setLoading(false) diff --git a/app/(public)/page.tsx b/app/(public)/page.tsx index 465ddec..c2bd09e 100644 --- a/app/(public)/page.tsx +++ b/app/(public)/page.tsx @@ -1,6 +1,6 @@ import Link from "next/link" 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 { Card, CardContent } from "@/components/ui/card" import { createClient } from "@/lib/supabase-server" diff --git a/app/(public)/products/page.tsx b/app/(public)/products/page.tsx index 040753d..6de627f 100644 --- a/app/(public)/products/page.tsx +++ b/app/(public)/products/page.tsx @@ -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 Image from "next/image" + const products = [ { diff --git a/app/(public)/signup/page.tsx b/app/(public)/signup/page.tsx index 40364e6..10646d1 100644 --- a/app/(public)/signup/page.tsx +++ b/app/(public)/signup/page.tsx @@ -1,7 +1,7 @@ "use client" import { useState } from "react" -import { useRouter } from "next/navigation" + import Link from "next/link" import { supabase } from "@/lib/supabase" import { Button } from "@/components/ui/button" @@ -11,7 +11,6 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } import { AlertCircle, Loader2, CheckCircle2 } from "lucide-react" export default function SignUpPage() { - const router = useRouter() const [email, setEmail] = useState("") const [password, setPassword] = useState("") const [loading, setLoading] = useState(false) @@ -76,7 +75,7 @@ export default function SignUpPage() { Hesap Oluştur - ParaKasa'ya katılmak için bilgilerinizi girin + ParaKasa'ya katılmak için bilgilerinizi girin diff --git a/app/layout.tsx b/app/layout.tsx index 2d099e5..e5e36e9 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,4 +1,4 @@ -import type { Metadata } from "next"; + import { Inter, Outfit } from "next/font/google"; import { Toaster } from "sonner"; import "./globals.css"; diff --git a/components/dashboard/category-form.tsx b/components/dashboard/category-form.tsx index 4bc5b76..ecf4fc5 100644 --- a/components/dashboard/category-form.tsx +++ b/components/dashboard/category-form.tsx @@ -62,8 +62,8 @@ export function CategoryForm({ initialData }: CategoryFormProps) { try { if (initialData) { const result = await updateCategory(initialData.id, data) - if ((result as any).error) { - toast.error((result as any).error) + if ('error' in result) { + toast.error(result.error) } else { toast.success(toastMessage) router.push(`/dashboard/categories`) @@ -71,15 +71,15 @@ export function CategoryForm({ initialData }: CategoryFormProps) { } } else { const result = await createCategory(data) - if ((result as any).error) { - toast.error((result as any).error) + if ('error' in result) { + toast.error(result.error) } else { toast.success(toastMessage) router.push(`/dashboard/categories`) router.refresh() } } - } catch (error) { + } catch { toast.error("Bir hata oluştu.") } finally { setLoading(false) @@ -90,14 +90,14 @@ export function CategoryForm({ initialData }: CategoryFormProps) { setLoading(true) try { const result = await deleteCategory(initialData!.id) - if ((result as any).error) { - toast.error((result as any).error) + if ('error' in result) { + toast.error(result.error) } else { toast.success("Kategori silindi.") router.push(`/dashboard/categories`) router.refresh() } - } catch (error) { + } catch { toast.error("Silme işlemi başarısız.") } finally { setLoading(false) diff --git a/components/dashboard/password-form.tsx b/components/dashboard/password-form.tsx index 09048d4..2d473a4 100644 --- a/components/dashboard/password-form.tsx +++ b/components/dashboard/password-form.tsx @@ -57,7 +57,7 @@ export function PasswordForm() { toast.success("Şifreniz başarıyla güncellendi.") form.reset() router.refresh() - } catch (error) { + } catch { toast.error("Bir sorun oluştu.") } finally { setLoading(false) diff --git a/components/dashboard/product-form.tsx b/components/dashboard/product-form.tsx index 4d0112b..59b89b9 100644 --- a/components/dashboard/product-form.tsx +++ b/components/dashboard/product-form.tsx @@ -94,7 +94,7 @@ export function ProductForm({ initialData }: ProductFormProps) { toast.success(initialData ? "Ürün güncellendi" : "Ürün başarıyla oluşturuldu") router.push("/dashboard/products") router.refresh() - } catch (error) { + } catch { toast.error("Bir aksilik oldu") } finally { setLoading(false) diff --git a/components/dashboard/sidebar.tsx b/components/dashboard/sidebar.tsx index f583aca..5b67430 100644 --- a/components/dashboard/sidebar.tsx +++ b/components/dashboard/sidebar.tsx @@ -43,7 +43,7 @@ const sidebarItems = [ }, ] -interface SidebarProps extends React.HTMLAttributes { } +type SidebarProps = React.HTMLAttributes export function Sidebar({ className }: SidebarProps) { const pathname = usePathname() diff --git a/components/dashboard/site-settings-form.tsx b/components/dashboard/site-settings-form.tsx index da8036b..5f88a4e 100644 --- a/components/dashboard/site-settings-form.tsx +++ b/components/dashboard/site-settings-form.tsx @@ -54,7 +54,6 @@ export function SiteSettingsForm({ initialData }: SiteSettingsFormProps) { const onSubmit = async (data: SettingsFormValues) => { setLoading(true) try { - // @ts-ignore const result = await updateSiteSettings(data) if (result.error) { @@ -64,7 +63,7 @@ export function SiteSettingsForm({ initialData }: SiteSettingsFormProps) { toast.success("Site ayarları güncellendi.") router.refresh() - } catch (error) { + } catch { toast.error("Bir sorun oluştu.") } finally { setLoading(false) diff --git a/components/dashboard/user-form.tsx b/components/dashboard/user-form.tsx index 2d6ca02..21db539 100644 --- a/components/dashboard/user-form.tsx +++ b/components/dashboard/user-form.tsx @@ -32,23 +32,34 @@ const userSchema = z.object({ 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."), 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(), role: z.enum(["admin", "user"]), phone: z.string().optional(), -}).refine((data) => { +}).superRefine((data, ctx) => { // 1. Password match check 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, - // but the matching check is now here. - return true -}, { - message: "Şifreler eşleşmiyor.", - path: ["confirmPassword"], -}) + + // 2. New user password requirement check + // If there is no ID (we can't easily check for ID here in schema without context, + // but typically empty password on CREATE is invalid unless handled elsewhere. + // However, the component logic implies 'initialData' determines edit/create mode. + // For pure schema validation, we often make password required for create, optional for edit. + // 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 @@ -113,12 +124,8 @@ export function UserForm({ initialData, mode = "admin" }: UserFormProps) { }) } else { // Admin create mode - if (!data.password || data.password.length < 6) { - toast.error("Yeni kullanıcı için şifre gereklidir (min 6 karakter).") - setLoading(false) - return - } - result = await createUser(data.firstName, data.lastName, data.email, data.password, data.role, data.phone) + // Password requirement is now handled by Zod Schema + result = await createUser(data.firstName, data.lastName, data.email, data.password!, data.role, data.phone) } if (result.error) { @@ -136,7 +143,7 @@ export function UserForm({ initialData, mode = "admin" }: UserFormProps) { if (mode === "admin") { router.push("/dashboard/users") } - } catch (error) { + } catch { toast.error("Bir sorun oluştu.") } finally { setLoading(false) diff --git a/components/dashboard/user-nav.tsx b/components/dashboard/user-nav.tsx index bba07c5..7f46549 100644 --- a/components/dashboard/user-nav.tsx +++ b/components/dashboard/user-nav.tsx @@ -18,14 +18,7 @@ import { } from "@/components/ui/dropdown-menu" import { createBrowserClient } from "@supabase/ssr" 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 { user: { diff --git a/lint.log b/lint.log new file mode 100644 index 0000000..f918915 Binary files /dev/null and b/lint.log differ diff --git a/package.json b/package.json index ea6cd5c..7db7e91 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint" + "lint": "next lint" }, "dependencies": { "@hookform/resolvers": "^5.2.2", @@ -49,4 +49,4 @@ "tw-animate-css": "^1.4.0", "typescript": "^5" } -} +} \ No newline at end of file