Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cc02d4c9c7 | |||
| 4b71e10d82 | |||
| ba4d1d23a4 | |||
| fd8dceab1a | |||
| ee1cf4807d |
84
README.md
84
README.md
@@ -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!"),
|
||||
});
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
28
mds/2026-01-30-setup-and-docs.md
Normal file
28
mds/2026-01-30-setup-and-docs.md
Normal 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.
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
12
prompt.md
12
prompt.md
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
];
|
||||
|
||||
16
src/provider/react-query-provider.tsx
Normal file
16
src/provider/react-query-provider.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
81
src/proxy.ts
81
src/proxy.ts
@@ -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*'],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user