Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions .claude/skills/api/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
name: api
description: "Next.js API route handler olusturur (App Router). Triggers: api, endpoint, route handler, REST, GET, POST, PUT, DELETE, CRUD, backend, server action."
argument-hint: route-yolu - Aciklama ve HTTP metodlari
---

Asagidaki bilgilere gore bir Next.js API route handler olustur:

$ARGUMENTS

## Kurallar

### Dosya Konumu
- Route handler: `app/api/[route-yolu]/route.ts`
- Ic ice route: `app/api/[ust]/[alt]/route.ts`
- Dinamik route: `app/api/[route]/[id]/route.ts`

### Route Handler Yapisi
```ts
import { NextRequest, NextResponse } from "next/server";

export async function GET(request: NextRequest) {
try {
return NextResponse.json({ data }, { status: 200 });
} catch (error) {
console.error("[GET /api/route]:", error);
return NextResponse.json({ error: "Sunucu hatasi" }, { status: 500 });
}
}

export async function POST(request: NextRequest) {
try {
const body = await request.json();
return NextResponse.json({ data }, { status: 201 });
} catch (error) {
console.error("[POST /api/route]:", error);
return NextResponse.json({ error: "Sunucu hatasi" }, { status: 500 });
}
}
```

### Dinamik Route Handler
```ts
import { NextRequest, NextResponse } from "next/server";

interface RouteParams {
params: Promise<{ id: string }>;
}

export async function GET(request: NextRequest, { params }: RouteParams) {
const { id } = await params;
try {
return NextResponse.json({ data }, { status: 200 });
} catch (error) {
console.error(`[GET /api/route/${id}]:`, error);
return NextResponse.json({ error: "Kayit bulunamadi" }, { status: 404 });
}
}

export async function PUT(request: NextRequest, { params }: RouteParams) {
const { id } = await params;
const body = await request.json();
try {
return NextResponse.json({ data }, { status: 200 });
} catch (error) {
console.error(`[PUT /api/route/${id}]:`, error);
return NextResponse.json({ error: "Sunucu hatasi" }, { status: 500 });
}
}

export async function DELETE(request: NextRequest, { params }: RouteParams) {
const { id } = await params;
try {
return NextResponse.json({ message: "Basariyla silindi" }, { status: 200 });
} catch (error) {
console.error(`[DELETE /api/route/${id}]:`, error);
return NextResponse.json({ error: "Sunucu hatasi" }, { status: 500 });
}
}
```

### Zorunlu Kurallar
1. **Server-only**: `"use client"` KULLANMA
2. **TypeScript**: Request body, response ve params icin tip tanimla
3. **Next.js 16 params**: `params` artik `Promise` - `await` ile eris
4. **Hata yonetimi**: Her handleri try/catch ile sar
5. **HTTP status kodlari**: Dogru kodlari kullan (200, 201, 400, 404, 500)
6. **Loglama**: Hatalarda `console.error` ile endpoint bilgisini logla
7. **Validasyon**: POST/PUT bodysini islemeden once dogrula

### Query Parameter Okuma
```ts
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const page = parseInt(searchParams.get("page") || "1", 10);
const limit = parseInt(searchParams.get("limit") || "10", 10);
}
```
105 changes: 105 additions & 0 deletions .claude/skills/component/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
name: component
description: "React component olusturur. Triggers: component, button, card, modal, input, badge, sidebar, navbar, header, footer, UI element, widget, bilesen olustur."
argument-hint: ComponentAdi - Kisa aciklama
---

Asagidaki bilgilere gore bir React component olustur:

$ARGUMENTS

## Kurallar

### Dosya Konumu
- Temel UI componenti ise (Button, Input, Card, Modal, Badge...): `components/ui/ComponentAdi.tsx`
- Ozellige ozel component ise: `components/ComponentAdi.tsx`
- Eger aciklamada farkli bir konum belirtilmisse, onu kullan

### Component Yapisi
```tsx
// Gerekli ise "use client" en uste ekle (useState, useEffect, event handler varsa)

interface ComponentAdiProps {
// Props tanimla
}

export default function ComponentAdi({ ...props }: ComponentAdiProps) {
return (
// JSX
);
}
```

### Zorunlu Kurallar
1. **TypeScript**: Props icin interface tanimla, `any` kullanma
2. **Tailwind CSS**: Stil icin sadece Tailwind classlari kullan, inline style veya CSS module kullanma
3. **Server Component varsayilan**: `"use client"` sadece interaktivite gerekiyorsa ekle
4. **Default export** kullan
5. **Dosya adi** PascalCase olmali
6. **Responsive**: Mobile-first yaklasim, `sm:`, `md:`, `lg:` breakpointleri kullan
7. **Dark mode**: `dark:` prefix ile karanlik tema destegi ekle
8. **Erisilebilirlik**: Uygun ARIA attributeleri, semantik HTML etiketleri kullan
9. **Import path**: `@/` alias kullan (ornek: `@/components/ui/Button`)

### Props Tasarimi
- Opsiyonel proplar icin `?` kullan
- `className` propu kabul et (kullanicinin stil ekleyebilmesi icin)
- Event handlerlar icin uygun React tipi kullan (`React.MouseEvent`, `React.ChangeEvent` vb.)
- children propu gerekliyse `React.ReactNode` tipinde tanimla

### Ornek Cikti Yapisi
Eger `/component Button - Tiklanabilir buton componenti, variant ve size destegi` yazildiysa:

```tsx
"use client";

interface ButtonProps {
children: React.ReactNode;
variant?: "primary" | "secondary" | "outline" | "ghost";
size?: "sm" | "md" | "lg";
disabled?: boolean;
className?: string;
onClick?: React.MouseEventHandler<HTMLButtonElement>;
}

export default function Button({
children,
variant = "primary",
size = "md",
disabled = false,
className = "",
onClick,
}: ButtonProps) {
const baseStyles =
"inline-flex items-center justify-center rounded-lg font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none";

const variants = {
primary: "bg-foreground text-background hover:bg-foreground/90 focus:ring-foreground",
secondary:
"bg-zinc-100 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:hover:bg-zinc-700",
outline: "border border-zinc-300 hover:bg-zinc-100 dark:border-zinc-700 dark:hover:bg-zinc-800",
ghost: "hover:bg-zinc-100 dark:hover:bg-zinc-800",
};

const sizes = {
sm: "h-8 px-3 text-sm",
md: "h-10 px-4 text-base",
lg: "h-12 px-6 text-lg",
};

return (
<button
className={`${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
}
```

Dosyayi olusturduktan sonra kullaniciya bilgi ver:
- Dosya yolu
- Kullanim ornegi (import + JSX)
- Eklenen proplarin listesi
105 changes: 105 additions & 0 deletions .claude/skills/form/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
name: form
description: "Validasyonlu form componenti olusturur. Triggers: form, login, register, signup, giris, kayit, contact form, input, validasyon, submit, form olustur."
argument-hint: FormAdi - Form alanlari ve aciklama
---

Asagidaki bilgilere gore validasyonlu bir form componenti olustur:

$ARGUMENTS

## Kurallar

### Dosya Konumu
- Form componentleri: `components/forms/FormAdi.tsx`

### Form Yapisi
```tsx
"use client";

import { useState, type FormEvent } from "react";

interface FormAdiData {
// Form alanlari
}

interface FormAdiErrors {
// Hata alanlari (her alan opsiyonel string)
}

interface FormAdiProps {
onSubmit: (data: FormAdiData) => void | Promise<void>;
className?: string;
}

export default function FormAdi({ onSubmit, className = "" }: FormAdiProps) {
const [formData, setFormData] = useState<FormAdiData>({});
const [errors, setErrors] = useState<FormAdiErrors>({});
const [isSubmitting, setIsSubmitting] = useState(false);

function validate(data: FormAdiData): FormAdiErrors {
const newErrors: FormAdiErrors = {};
return newErrors;
}

async function handleSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
const validationErrors = validate(formData);
setErrors(validationErrors);
if (Object.keys(validationErrors).length > 0) return;
setIsSubmitting(true);
try {
await onSubmit(formData);
} catch {
} finally {
setIsSubmitting(false);
}
}

return (
<form onSubmit={handleSubmit} className={className} noValidate>
{/* Form alanlari */}
</form>
);
}
```

### Zorunlu Kurallar
1. **"use client"**: Formlar her zaman client component
2. **TypeScript**: Form data ve error tipleri icin interface tanimla
3. **Tailwind CSS**: Tum stiller Tailwind ile
4. **Validasyon**: Client-side validasyon ZORUNLU
5. **Hata gosterimi**: Her alanin altinda hata mesaji goster
6. **Loading durumu**: Submit sirasinda buton disabled + yukleniyor gostergesi
7. **Erisilebilirlik**: `<label>` + `htmlFor`, `aria-invalid`, `aria-describedby`
8. **Responsive**: Mobileda tam genislik, desktopta uygun genislik
9. **Dark mode**: Tum form elemanlari dark mode destekli

### Input Stili Sablonu
```tsx
<div>
<label htmlFor="email" className="mb-1 block text-sm font-medium text-zinc-700 dark:text-zinc-300">
E-posta
</label>
<input
id="email"
type="email"
value={formData.email}
onChange={(e) => setFormData((prev) => ({ ...prev, email: e.target.value }))}
className={`w-full rounded-lg border px-3 py-2 text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-foreground/20 dark:bg-zinc-900 ${
errors.email ? "border-red-500 focus:ring-red-500/20" : "border-zinc-300 dark:border-zinc-700"
}`}
aria-invalid={!!errors.email}
aria-describedby={errors.email ? "email-error" : undefined}
/>
{errors.email && (
<p id="email-error" className="mt-1 text-sm text-red-500">{errors.email}</p>
)}
</div>
```

### Yaygin Validasyon Kurallari
- **Zorunlu alan**: `if (!value.trim()) errors.field = "Bu alan zorunludur";`
- **E-posta**: `if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) errors.email = "Gecerli bir e-posta girin";`
- **Min uzunluk**: `if (value.length < 8) errors.field = "En az 8 karakter olmali";`
- **Sifre eslesmesi**: `if (password !== confirmPassword) errors.confirmPassword = "Sifreler eslesmiyor";`
Loading
Loading