Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d257a96
init nextjs
CBrather Oct 17, 2025
d4a7948
configure AI tools
CBrather Oct 17, 2025
09c0557
set up chadcn with growteer theme
CBrather Oct 17, 2025
87c6b85
implement landing page
CBrather Oct 18, 2025
96890c6
add testing for the homepage
CBrather Oct 18, 2025
f78932d
add coverage reporting and ci pipeline
CBrather Oct 18, 2025
a688cfc
add initial ADRs
cbr-sematell Jan 16, 2026
257d313
init project architecture setup
cbr-sematell Jan 16, 2026
0a14f81
add cursor rules
cbr-sematell Jan 16, 2026
7cce554
clean readme and activate formatOnSave for vscode
cbr-sematell Jan 22, 2026
a061fab
fix coloring of landing card icons
cbr-sematell Jan 22, 2026
f21a2f5
redirect from index to /[defaultLocale]
cbr-sematell Jan 22, 2026
a3ecdb7
set up shadcn mcp
cbr-sematell Jan 23, 2026
24ef311
refactor ui to use shadcn components
cbr-sematell Jan 23, 2026
fb42014
refactor(api): separate client vs request config, use Headers, add co…
cbr-sematell Jan 24, 2026
d311959
fix: resolve TypeScript errors, simplify API client, add shadcn input…
cbr-sematell Jan 24, 2026
9439668
test: use next-intl normally with NextIntlClientProvider
cbr-sematell Jan 25, 2026
3fb2220
fix missing lang attribute in html
cbr-sematell Jan 26, 2026
8de63ff
Bump the npm_and_yarn group across 1 directory with 2 updates
dependabot[bot] Nov 15, 2025
c2bf918
improve cursor instructions with new learnings
cbr-sematell Jan 26, 2026
b9c516a
add agent skills and improve instructions
cbr-sematell Jan 27, 2026
6c3aa06
upgrade nextjs to v16
cbr-sematell Jan 28, 2026
b67a476
Bump the npm_and_yarn group across 1 directory with 5 updates
dependabot[bot] Jan 28, 2026
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
10 changes: 10 additions & 0 deletions .cursor/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"mcpServers": {
"shadcn": {
"command": "shadcn",
"args": [
"mcp"
]
}
}
}
59 changes: 59 additions & 0 deletions .cursor/rules/architecture.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
description: Every time a new feature, file or folder has to be created
alwaysApply: false
---
# Architecture Rules

## Folder Structure

Follow this exact structure for consistency:

```
src/
├── app/ # Next.js App Router (routes only)
│ └── [locale]/ # Locale-based routing
│ ├── (auth)/ # Route groups with parentheses
│ ├── (dashboard)/
│ └── layout.tsx # Locale-specific layout
├── components/ # Shared UI components (not route-specific)
│ ├── ui/ # Shadcn components
│ ├── layout/ # Layout components (Header, Footer, Sidebar)
│ ├── forms/ # Reusable form components
├── features/ # Feature modules (feature-sliced architecture)
│ └── {feature-name}/
│ ├── api/ # API client functions
│ ├── components/ # Feature-specific UI components
│ ├── hooks/ # Feature-specific hooks
│ ├── lib/ # Feature-specific utilities
│ └── types/ # Feature-specific types
├── lib/ # Core utilities and infrastructure
│ ├── api/ # API client (centralized)
│ ├── i18n/ # Internationalization config
│ ├── react-query/ # React Query provider
│ ├── validation/ # Zod schemas and form helpers
│ └── pwa/ # PWA utilities
├── hooks/ # Shared React hooks (not feature-specific)
├── store/ # Zustand stores (global state)
├── types/ # Global TypeScript types
└── locales/ # Translation files (next-intl)
```

## Naming Conventions

- **Folders**: camelCase for features/lib (`auth`, `apiClient`), kebab-case for routes
- **Components**: PascalCase (`UserProfile.tsx`)
- **Files**: camelCase for utilities/hooks (`useAuth.ts`), PascalCase for components
- **Route groups**: Use parentheses `(auth)`, `(dashboard)` to organize without affecting URLs
- **NO underscore prefixes** - Next.js App Router distinguishes routes via `(folderName)` syntax

## Feature-Sliced Architecture

Each feature in `features/` must be self-contained:

- `api/` - API client functions for this feature
- `components/` - Feature-specific UI components
- `hooks/` - Feature-specific React hooks
- `lib/` - Feature-specific utilities/logic
- `types/` - Feature-specific TypeScript types

**Principle**: Features should be independent and reusable. Import shared utilities from `lib/`, not from other features.
129 changes: 129 additions & 0 deletions .cursor/rules/data-fetching.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
description: When working on anything related to API calls / data fetching
alwaysApply: false
---
# Data Fetching & API Rules

## Centralized API Client

**ALWAYS use the centralized API client** from `@/lib/api/client`:

```typescript
// ✅ CORRECT: Use centralized client
import { apiClient } from "@/lib/api";

const response = await apiClient.post<RequestType, ResponseType>("/endpoint", data);
```

### API Client Features

The `apiClient` automatically handles:
- Authentication tokens (from Zustand auth store)
- Error handling and 401 redirects
- Request/response interceptors
- Timeout handling
- Base URL configuration

### API Functions Location

- **Feature-specific API functions**: `features/{feature}/api/`
- **Use centralized client**: Import `apiClient` in feature API files
- **Type safety**: Use TypeScript generics for request/response types

```typescript
// ✅ CORRECT: Feature API function
// features/users/api/users.ts
import { apiClient } from "@/lib/api";
import type { User, CreateUserRequest } from "../types";

export async function createUser(data: CreateUserRequest) {
return apiClient.post<CreateUserRequest, User>("/users", data);
}
```

### Environment Variables

- Use `NEXT_PUBLIC_API_BASE_URL` for API base URL
- Defaults to `http://localhost:3001/api` if not set
- API client reads from `API_BASE_URL` constant in `lib/constants/`

## Type Generation

- **Auto-generate types** from OpenAPI/GraphQL schema when available
- Generated types go in `lib/api/generated/`
- See `scripts/generate-api-types.md` for setup
- **DO NOT manually write API types** if schema is available - generate them

## Data Fetching Strategy

Use a **hybrid approach** based on context:

### Server Components (Default)

**Use Server Components for:**
- Initial page data
- SEO-critical content
- Public data that doesn't change frequently
- Data that benefits from SSR

```typescript
// ✅ CORRECT: Fetch in Server Components for initial data
export default async function Page() {
const posts = await apiClient.get("/posts");
return <PostsList posts={posts.data} />;
}
```

### TanStack Query (Client Components)

**Use TanStack Query for:**
- User-specific data after hydration
- Real-time updates
- Optimistic updates
- Data that needs caching/refetching
- Interactive features

```typescript
// ✅ CORRECT: Use TanStack Query for dynamic/interactive data
"use client";
import { useQuery } from "@tanstack/react-query";
import { apiClient } from "@/lib/api";

export function UserProfile({ userId }: { userId: string }) {
const { data: user } = useQuery({
queryKey: ["user", userId],
queryFn: () => apiClient.get(`/users/${userId}`),
});
return <div>{user?.data?.name}</div>;
}
```

**Decision Tree:**
```
Need data?
├─ Initial page load? → Server Component
├─ User-specific after login? → TanStack Query
├─ Real-time updates? → TanStack Query
├─ SEO-critical? → Server Component
└─ Needs caching/refetching? → TanStack Query
```

**Implementation guides:**
- Server Components: See `nextjs` skill
- TanStack Query: See `tanstack-query` skill

## Error Handling

- **API functions**: Return structured `APIResponse<T>` with `success` and `error` fields
- **Components/hooks**: Display errors, retry logic, fallback UI
- Always check `response.success` before using `response.data`

```typescript
// ✅ CORRECT: Error handling pattern
const response = await apiClient.get<User>("/user/me");
if (response.success && response.data) {
// Use response.data
} else {
// Handle response.error
}
```
53 changes: 53 additions & 0 deletions .cursor/rules/general.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
alwaysApply: true
---

# General Rules & Best Practices

## Simplicity First

Before implementing any solution, ask yourself:

1. **Is there a simpler way?** - Can this be done with less code, fewer components, or fewer dependencies?
2. **Can this be server-side?** - Server Components are simpler, faster, and better for SEO
3. **Do I really need this?** - Is the complexity justified, or is there a built-in solution?

**For complete problem-solving methodology**, see `problem-solving` skill.

## Tech Stack

- **Framework**: Next.js 16 with App Router
- **Language**: TypeScript (strict mode), see `coding-standards` skill for best practices
- **Package Manager**: pnpm
- **UI Library**: Shadcn/ui components
- **Styling**: Tailwind CSS v4

## Core Libraries

- **i18n**: next-intl
- **Server State**: TanStack Query v5
- **Client State**: Zustand
- **Forms & Validation**: React Hook Form with Zod

## File Organization

- **Avoid barrel files** - Don't use `index.ts` that re-exports from multiple files
- **Feature ownership** - Each feature in `features/` is self-contained

### Manual Types

- Global types in `src/types/` folder
- Feature-specific types in `features/{feature}/types/`

## Error Handling

- Handle errors gracefully
- Provide fallback UI for errors
- Use error boundaries where appropriate
- Log errors for debugging (use consistent logging strategy)

## Testing

- Use jest for unit and integration tests
- Use React Testing Library for component tests
- Use MSW for API mocking in tests
66 changes: 66 additions & 0 deletions .cursor/rules/i18n.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
description: When adding or changing visible texts in the UI
alwaysApply: false
---
# Internationalization Rules

## i18n Library

Use **next-intl** for all internationalization:

## Locale Structure

- **Locales folder**: `src/locales/{locale}/`
- **Share common texts** Texts used across features live in `src/locales/{locale}/common.json`
- **Organized by feature**: One translation file per language per feature, e.g. `src/locales/{locale}/{feature}.json`
- **Supported locales**: defined in `lib/i18n/routing.ts`
- **Route structure**: All routes under `app/[locale]/`

## Usage Patterns

### Server Components (Preferred)

```typescript
// ✅ CORRECT: Use getTranslations in Server Components
import { getTranslations } from "next-intl/server";

export default async function Page() {
const t = await getTranslations();
return <h1>{t("common.welcome")}</h1>;
}
```

### Client Components

```typescript
// ✅ CORRECT: Use useTranslations in Client Components
"use client";
import { useTranslations } from "next-intl";

export function Component() {
const t = useTranslations();
return <button>{t("common.save")}</button>;
}
```

### Navigation

```typescript
// ✅ CORRECT: Use next-intl Link for locale-aware navigation
import { Link } from "@/lib/i18n/routing";

<Link href="/dashboard">Dashboard</Link>
```

## Middleware

- Middleware handles locale detection and routing
- Located at project root: `middleware.ts`
- Automatically redirects to appropriate locale

## Rules

- **NEVER use Next.js `Link` directly** - use `@/lib/i18n/routing` Link
- **Always use translation functions** - don't hardcode strings
- **Add translations** - for all supported locales
- **Use nested keys** - organize translations logically (`common.*`, `navigation.*`)
60 changes: 60 additions & 0 deletions .cursor/rules/state-management.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
description: When working on components or otherwise dealing with state
alwaysApply: false
---
# State Management Rules

## State Management Strategy

Use this hybrid approach for all state:

### 1. TanStack Query (React Query) - Server State
- Use for server/API data
- Automatic caching, refetching, synchronization
- Use in Client Components only
- Preferred for user-specific data after hydration

```typescript
// ✅ CORRECT: Use TanStack Query for API data
import { useQuery, useMutation } from "@tanstack/react-query";

const { data } = useQuery({
queryKey: ["users", userId],
queryFn: () => fetchUser(userId),
});
```

### 2. Zustand - Global Client State
- Use for auth state, UI preferences, real-time features
- Persist state when needed (use `persist` middleware)

```typescript
// ✅ CORRECT: Use Zustand for global client state
import { create } from "zustand";
import { persist } from "zustand/middleware";

export const useAuthStore = create<AuthState>()(
persist((set) => ({ /* ... */ }), { name: "auth-storage" })
);
```

### 3. React Context - App-Wide Providers Only
- Use ONLY for truly app-wide providers (theme, i18n context)
- Avoid for feature-specific state - use Zustand instead

### 4. useState - Component-Scoped State
- Use for component-only state that doesn't need sharing
- Simple form state, UI toggles, etc.

## Rules

- **Server state goes in TanStack Query** - don't duplicate in Zustand
- **Zustand stores must be in `src/store/` folder** - named as `{name}Store.ts`
- **Use TanStack Query for optimistic updates** - not Zustand
- **Persist auth state** - use Zustand's `persist` middleware
- **Clear TanStack Query cache on logout** - use `queryClient.clear()`

## Implementation Guides

- **TanStack Query**: See `tanstack-query` skill for setup, patterns, and best practices
- **Zustand**: See `zustand-state-management` skill for store creation, middleware, and Next.js SSR handling
Loading