Compare commits

5 Commits

Author SHA1 Message Date
cc02d4c9c7 Merge branch 'example'
All checks were successful
UI Deploy (Next-Auth Support) 🎨 / build-and-deploy (push) Successful in 2m21s
2026-01-30 03:39:25 +03:00
4b71e10d82 main
Some checks failed
UI Deploy (Next-Auth Support) 🎨 / build-and-deploy (push) Has been cancelled
2026-01-30 03:39:17 +03:00
ba4d1d23a4 main
All checks were successful
UI Deploy (Next-Auth Support) 🎨 / build-and-deploy (push) Successful in 3m27s
2026-01-30 03:34:53 +03:00
fd8dceab1a main
All checks were successful
UI Deploy (Next-Auth Support) 🎨 / build-and-deploy (push) Successful in 2m39s
2026-01-30 03:31:18 +03:00
ee1cf4807d src/app/[locale]/(auth)/signin/page.tsx Güncelle
All checks were successful
UI Deploy (Next-Auth Support) 🎨 / build-and-deploy (push) Successful in 4m8s
2026-01-30 01:50:30 +03:00
9 changed files with 176 additions and 79 deletions

View File

@@ -212,39 +212,79 @@ toaster.error({ title: "Error", description: "Something went wrong." });
---
## 📡 API Integration
## 📡 API Integration (Standard Architecture)
### Creating API Clients
The project follows a strict **Service + Hook** pattern for API integration to ensure separation of concerns, type safety, and efficient caching.
```tsx
// src/lib/api/auth/login/queries.ts
import { createApiClient } from "../create-api-client";
**🏆 Golden Standard Reference:** [`src/lib/api/example`](src/lib/api/example)
const api = createApiClient("/api/backend/auth");
### 🏗️ Architecture Pattern
export const loginUser = async (data: LoginDto) => {
const response = await api.post("/login", data);
return response.data;
For every domain (e.g., `auth`, `users`), we strictly follow this 4-layer structure:
| Layer | File | Purpose |
|-------|------|---------|
| **1. Types** | `types.ts` | **Contract:** pure TypeScript interfaces/DTOs matching Backend. |
| **2. Service** | `service.ts` | **Logic:** Pure functions calling `apiRequest`. Independent of React. |
| **3. Hooks** | `use-hooks.ts` | **State:** React Query wrappers (`useQuery`, `useMutation`) for caching & state. |
| **4. Barrel** | `index.ts` | **Public API:** Central export point for the module. |
### 📝 Implementation Guide
#### 1. Define Service (`service.ts`)
Uses `apiRequest` wrapper which handles client selection (`auth`, `core`, etc.), base URLs, and error normalization.
```ts
// src/lib/api/example/auth/service.ts
import { apiRequest } from "@/lib/api/api-service";
import { LoginDto, AuthResponse } from "./types";
const login = (data: LoginDto) => {
return apiRequest<ApiResponse<AuthResponse>>({
url: "/auth/login",
client: "auth", // Selects the correct axios instance from clientMap
method: "post",
data,
});
};
export const authService = { login };
```
### React Query Integration
#### 2. Create Hook (`use-hooks.ts`)
Wraps service in TanStack Query for loading states, caching, and invalidation.
```ts
// src/lib/api/example/auth/use-hooks.ts
import { useMutation } from "@tanstack/react-query";
import { authService } from "./service";
export const AuthQueryKeys = {
all: ["auth"] as const,
};
export function useLogin() {
return useMutation({
mutationFn: (data: LoginDto) => authService.login(data),
});
}
```
#### 3. Use in Components
Components only interact with the Hooks, never the Service or Axios directly.
```tsx
import { useQuery, useMutation } from "@tanstack/react-query";
import { useLogin } from "@/lib/api/example"; // Import from barrel
// Query hook
export const useUsers = () =>
useQuery({
queryKey: ["users"],
queryFn: () => apiClient.get("/users"),
});
export function LoginForm() {
const { mutate, isPending } = useLogin();
// Mutation hook
export const useCreateUser = () =>
useMutation({
mutationFn: (data: CreateUserDto) => apiClient.post("/users", data),
});
const handleSubmit = (data) => {
mutate(data, {
onSuccess: () => console.log("Logged in!"),
});
};
}
```
---

View File

@@ -0,0 +1,28 @@
# AI Session Log - 2026-01-30
## Overview
This session focused on improving project documentation and establishing standard practices for AI interactions.
## 1. API Infrastructure Documentation
**Request:** Document the technological infrastructure and API service pattern in `src/lib/api/example` within `README.md`.
**Changes:**
- Updated `README.md` to replace the generic "API Integration" section with a detailed "Standard Architecture" guide.
- Established `src/lib/api/example` as the canonical reference (Golden Standard).
- Defined the 4-layer architecture: Types, Service, Hooks, and Barrel.
- Added code examples for `service.ts` and `use-hooks.ts`.
## 2. MDS Documentation Standard Setup
**Request:** Create an `mds` folder and ensure the AI always documents chat and changes there.
**Changes:**
- Created `mds/` directory.
- Updated `prompt.md` with a new section "📝 AI Documentation (MDS Standard)" to instruct future AI agents to use this folder.
- Created this log file `mds/2026-01-30-setup-and-docs.md` as the first entry.
## Summary
The project now has better architectural documentation in the README and a mechanism (via `prompt.md` and `mds/`) to ensure future AI modifications are well-documented.

View File

@@ -3,7 +3,7 @@
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "next dev --webpack --experimental-https -p 3001",
"dev": "next dev --webpack -p 3001",
"build": "next build --webpack",
"start": "next start",
"lint": "eslint"
@@ -41,4 +41,4 @@
"typescript": "^5"
},
"description": "Generated by Frontend CLI"
}
}

View File

@@ -8,12 +8,18 @@
1. **README.md** dosyasını oku - Projenin mimarisi, teknoloji stack'i ve kurulum adımlarını içerir.
```
README.md
```
## 📝 AI Documentation (MDS Standard)
**IMPORTANT:** Every AI session, chat, or major change MUST be documented in the `mds/` folder.
1. **Create/Update Log:** Create a markdown file (e.g., `mds/YYYY-MM-DD-topic.md`) or append to an existing daily log.
2. **Content:** Briefly summarize the user request, the changes made, and the rationale.
3. **Persistence:** This ensures a history of AI interactions and decisions is preserved within the repo.
---
## 📚 Projeyi Anlamak İçin Önce Oku
## 🎯 Referans Klasörü
`.claude/` klasörü best practice'ler, agent tanımları ve yardımcı scriptler içerir. Görev türüne göre ilgili referansları kullan:

View File

@@ -35,7 +35,7 @@ const schema = yup.object({
type SignInForm = yup.InferType<typeof schema>;
const defaultValues = {
email: "test@test.com.tr",
email: "test@test.com.ue",
password: "test1234",
};

View File

@@ -6,15 +6,18 @@ import { ColorModeProvider, type ColorModeProviderProps } from "./color-mode";
import { system } from "../../theme/theme";
import { Toaster } from "./feedback/toaster";
import TopLoader from "./top-loader";
import ReactQueryProvider from "@/provider/react-query-provider";
export function Provider(props: ColorModeProviderProps) {
return (
<SessionProvider>
<ChakraProvider value={system}>
<TopLoader />
<ColorModeProvider {...props} />
<Toaster />
</ChakraProvider>
<ReactQueryProvider>
<ChakraProvider value={system}>
<TopLoader />
<ColorModeProvider {...props} />
<Toaster />
</ChakraProvider>
</ReactQueryProvider>
</SessionProvider>
);
}

View File

@@ -1,15 +1,14 @@
export type NavChildItem = {
label: string;
href: string;
};
export type NavItem = {
label: string;
href: string;
children?: NavChildItem[];
protected?: boolean;
public?: boolean;
onlyPublic?: boolean;
visible?: boolean;
children?: NavItem[];
};
export const NAV_ITEMS: NavItem[] = [
{ label: "home", href: "/home" },
{ label: "about", href: "/about" },
{ label: "home", href: "/home", public: true },
{ label: "predictions", href: "/predictions", public: true },
];

View File

@@ -0,0 +1,16 @@
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactNode } from "react";
interface Props {
children: ReactNode;
}
export default function ReactQueryProvider({ children }: Props) {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
}

View File

@@ -1,50 +1,55 @@
import { NextResponse } from 'next/server';
import { withAuth } from 'next-auth/middleware';
import createIntlMiddleware from 'next-intl/middleware';
import { routing } from './i18n/routing';
import { NAV_ITEMS } from "@/config/navigation";
import { withAuth } from "next-auth/middleware";
import createMiddleware from "next-intl/middleware";
import { NextRequest } from "next/server";
import { routing } from "./i18n/routing";
const intlMiddleware = createIntlMiddleware(routing);
const publicPages = NAV_ITEMS.flatMap((item) => [
...(!item.protected ? [item.href] : []),
...(item.children
?.filter((child) => !child.protected)
.map((child) => child.href) ?? []),
]);
const publicPaths = ['/signin', '/signup', '/forgot-password', '/reset-password'];
const localizedPublicPaths = routing.locales.flatMap((locale) => publicPaths.map((path) => `/${locale}${path}`));
const allPublicPaths = [...publicPaths, ...localizedPublicPaths];
const handleI18nRouting = createMiddleware(routing);
export default withAuth(
async function middleware(req) {
const intlResponse = intlMiddleware(req);
if (intlResponse) return intlResponse;
const token = req.nextauth?.token;
const { pathname } = req.nextUrl;
// Kullanıcı giriş yaptıysa ve public bir sayfadaysa → home'a yönlendir
if (token && allPublicPaths.some((p) => pathname.startsWith(p))) {
const redirectUrl = new URL(`/${routing.defaultLocale}/home`, req.url);
return NextResponse.redirect(redirectUrl);
}
return NextResponse.next();
const authMiddleware = withAuth(
// Note that this callback is only invoked if
// the `authorized` callback has returned `true`
// and not for pages listed in `pages`.
function onSuccess(req) {
return handleI18nRouting(req);
},
{
pages: {
signIn: `/${routing.defaultLocale}/signin`,
},
callbacks: {
/**
* Eğer public route'taysa -> yetkilendirme kontrolü atla
* Aksi halde -> token gerekli
*/
authorized: ({ token, req }) => {
const { pathname } = req.nextUrl;
if (allPublicPaths.some((p) => pathname.startsWith(p))) {
return true; // public sayfa, izin ver
}
return !!token; // diğerleri için token gerekli
},
authorized: ({ token }) => token != null,
},
pages: {
signIn: "/home",
},
},
);
export default function proxy(req: NextRequest) {
// CRITICAL: Skip API routes entirely - they should not go through i18n or auth middleware
if (req.nextUrl.pathname.startsWith("/api/")) {
return; // Return undefined to pass through without modification
}
const publicPathnameRegex = RegExp(
`^(/(${routing.locales.join("|")}))?(${publicPages.flatMap((p) => (p === "/" ? ["", "/"] : p)).join("|")})/?$`,
"i",
);
const isPublicPage = publicPathnameRegex.test(req.nextUrl.pathname);
if (isPublicPage) {
return handleI18nRouting(req);
} else {
return (authMiddleware as any)(req);
}
}
export const config = {
matcher: ['/((?!api|trpc|_next|_vercel|.*\\..*).*)'],
matcher: "/((?!api|trpc|_next|_vercel|.*\\..*).*)",
// matcher: ['/', '/(de|en|tr)/:path*'],
};