# Typescript

- Write clean, clear, well-designed, explicit Typescript
- Make sure types are validated strictly
- Use implicit type inference, unless impossible
- Consider using classes for server-side services, but export a function instead of the class

```tsx
// service.ts
class UserService {
  getUser(id: number) {
    // ... implementation ...
    return { id, name: 'Example User' };
  }
}

export function createUserService() {
    return new UserService();
}
```

- Follow the Single Responsibility Principle (SRP). Each module/function/class should have one reason to change.
- Favor composition over inheritance.
- Handle errors gracefully using try/catch and appropriate error types.
- Keep functions short and focused.
- Use descriptive names for variables, functions, and classes.
- Avoid unnecessary complexity.
- Avoid using `any` type as much as possible. If necessary, use `unknown`
- Use enums only when appropriate. Consider union types of string literals as an alternative.
- Be aware of performance implications of your code.

# React

## Core Principles

- **Component-Driven Development**: Build applications as a composition of isolated, reusable components
- **One-Way Data Flow**: Follow React's unidirectional data flow pattern
- **Single Responsibility**: Each component should have a clear, singular purpose
- **TypeScript First**: Use TypeScript for type safety and better developer experience
- **Internationalization (i18n) By Default**: All user-facing text should be translatable

## React Components

### Component Structure

- Always use functional components with TypeScript
- Name components using PascalCase (e.g., `UserProfile`)
- Use named exports for components, not default exports
- Split components by responsibility and avoid "god components"
- Name files to match their component name (e.g., `user-profile.tsx`)

### Props

- Always type props using TypeScript interfaces or type aliases
- Use discriminated unions for complex prop types with conditional rendering
- Destructure props at the start of component functions
- Use prop spreading cautiously and only when appropriate
- Provide default props for optional parameters when it makes sense

```typescript
type ButtonProps = {
  variant: 'primary' | 'secondary' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  children: React.ReactNode;
  disabled?: boolean;
  onClick?: () => void;
};

function Button({ 
  variant, 
  size = 'md', 
  children, 
  disabled = false, 
  onClick 
}: ButtonProps) {
  // Component implementation
}
```

### State Management

- Keep state as local as possible
- Lift state up when multiple components need access
- Use Context sparingly and only for truly global state
- Prefer the "Container/Presenter" pattern when separating data and UI

```typescript
// Container component (manages data)
function UserProfileContainer() {
  const userData = useUserData();
  
  if (userData.isLoading) {
    return <LoadingSpinner />;
  }

  if (userData.error) {
    return <ErrorMessage error={userData.error} />;
  }
  
  return <UserProfilePresenter data={userData.data} />;
}

// Presenter component (renders UI)
function UserProfilePresenter({ data }: { data: UserData }) {
  return (
    <div>
      <h1>{data.name}</h1>
      {/* Rest of the UI */}
    </div>
  );
}
```

### Hooks

- Follow the Rules of Hooks (only call hooks at the top level, only call them from React functions)
- Create custom hooks for reusable logic
- Keep custom hooks focused on a single concern
- Name custom hooks with a 'use' prefix (e.g., `useUserProfile`)
- Extract complex effect logic into separate functions
- Always provide a complete dependencies array to `useEffect`

### Performance Optimization

- Apply `useMemo` for expensive calculations
- Use `useCallback` for functions passed as props to child components
- Split code using dynamic imports and `React.lazy()`

```typescript
const MemoizedComponent = React.memo(function Component(props: Props) {
  // Component implementation
});

// For expensive calculations
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

// For callback functions passed as props
const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);
```

### Internationalization (i18n)

- Always use the `Trans` component for text rendering (no hardcoded strings)
- Ensure all i18n keys are available in locale files
- Use namespaces to organize translations logically
- Include interpolation variables in translation keys
- Test UI with different languages, especially those with longer text

```typescript
// Correct
<Trans i18nKey="user:profile.welcomeMessage" values={{ name: user.name }} />

// Incorrect
<p>Welcome, {user.name}!</p>
```

## Server Components

### Fundamentals

- Server Components render React server-side and never run on the client
- Use Server Components as the default choice, especially for data fetching
- No use of hooks, browser APIs, or event handlers in Server Components
- No use of `useState`, `useEffect`, or any other React hooks
- Server Components can render Client Components but not vice versa

### Data Fetching

- Fetch data directly using async/await in Server Components
- Use Suspense boundaries around data-fetching components
- Apply security checks before fetching sensitive data
- Never pass sensitive data (API keys, tokens) to Client Components
- Use React's `cache()` function for caching data requests

### Error Handling

- Implement error boundaries at appropriate levels
- Use the Next.js `error.tsx` file for route-level error handling
- Create fallback UI for when data fetching fails
- Log server errors appropriately without exposing details to clients

### Streaming and Suspense

- Use React Suspense for progressive loading experiences if specified
- Implement streaming rendering for large or complex pages
- Structure components to enable meaningful loading states
- Prioritize above-the-fold content when using streaming

## Client Components

### Fundamentals

- Add the `'use client'` directive at the top of files for Client Components
- Keep Client Components focused on interactivity and browser APIs
- Use hooks appropriately following the Rules of Hooks
- Implement controlled components for form elements
- Handle all browser events in Client Components

### Data Fetching

- Use React Query (TanStack Query) for data fetching in Client Components
- Create custom hooks for data fetching logic (e.g., `useUserData`)
- Always handle loading, success, and error states

### Form Handling

- Use libraries like React Hook Form for complex forms
- Implement proper validation with libraries like Zod
- Create reusable form components
- Handle form submissions with loading and error states
- Use controlled components for form inputs

### Error Handling

- Implement error boundaries to catch and handle component errors if using client components
- Always handle network request errors
- Provide user-friendly error messages
- Log errors appropriately
- Implement retry mechanisms where applicable

```typescript
'use client';

import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

export function UserProfileWithErrorHandling() {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onReset={() => {
        // Reset application state here if needed
      }}
    >
      <UserProfile userId="123" />
    </ErrorBoundary>
  );
}
```


# Project Structure

```
apps/web/app/                          # Root directory (apps/web/app)
│
├── (marketing)/              # Marketing pages group
│   ├── _components/          # Shared components for marketing routes
│   │   ├── site-footer.tsx
│   │   ├── site-header.tsx
│   │   ├── site-navigation.tsx
│   │   └── site-page-header.tsx
│   │
│   ├── (legal)/              # Legal pages subgroup
│   │   ├── cookie-policy/
│   │   │   └── page.tsx
│   │   ├── privacy-policy/
│   │   │   └── page.tsx
│   │   └── terms-of-service/
│   │       └── page.tsx
│   │
│   ├── blog/                 # Blog section
│   │   ├── _components/      # Blog-specific components
│   │   │   ├── blog-pagination.tsx
│   │   │   ├── post-header.tsx
│   │   │   └── post-preview.tsx
│   │   ├── [slug]/           # Dynamic route for blog posts
│   │   │   └── page.tsx
│   │   └── page.tsx          # Blog listing page
│   │
│   ├── contact/              # Contact page
│   │   ├── _components/
│   │   │   └── contact-form.tsx
│   │   ├── _lib/             # Contact page utilities
│   │   │   ├── contact-email.schema.ts
│   │   │   └── server/
│   │   │       └── server-actions.ts
│   │   └── page.tsx
│   │
│   ├── docs/                 # Documentation pages
│   │   ├── _components/
│   │   ├── _lib/
│   │   │   ├── server/
│   │   │   │   └── docs.loader.ts
│   │   │   └── utils.ts
│   │   ├── [slug]/
│   │   │   └── page.tsx
│   │   ├── layout.tsx        # Layout specific to docs section
│   │   └── page.tsx
│   │
│   ├── faq/
│   │   └── page.tsx
│   │
│   ├── pricing/
│   │   └── page.tsx
│   │
│   ├── layout.tsx            # Layout for all marketing pages
│   ├── loading.tsx           # Loading state for marketing pages
│   └── page.tsx              # Home/landing page
│
├── (auth)/                   # Authentication pages group
│   ├── callback/             # Auth callback routes
│   │   ├── error/
│   │   │   └── page.tsx
│   │   └── route.ts          # API route handler for auth callback
│   │
│   ├── confirm/
│   │   └── route.ts
│   │
│   ├── password-reset/
│   │   └── page.tsx
│   │
│   ├── sign-in/
│   │   └── page.tsx
│   │
│   ├── sign-up/
│   │   └── page.tsx
│   │
│   ├── verify/
│   │   └── page.tsx
│   │
│   ├── layout.tsx            # Layout for auth pages
│   └── loading.tsx           # Loading state for auth pages
│
├── admin/                    # Admin section
│   ├── _components/
│   │   ├── admin-sidebar.tsx
│   │   └── mobile-navigation.tsx
│   │
│   ├── accounts/
│   │   ├── [id]/
│   │   │   └── page.tsx
│   │   └── page.tsx
│   │
│   ├── layout.tsx
│   ├── loading.tsx
│   └── page.tsx
│
├── api/                      # API routes
│   ├── billing/
│   │   └── webhook/
│   │       └── route.ts
│   │
│   └── db/
│       └── webhook/
│           └── route.ts
│
├── home/                     # User dashboard area
│   ├── (user)/               # Personal user routes
│   │   ├── _components/      # User dashboard components
│   │   │   ├── home-account-selector.tsx
│   │   │   └── home-sidebar.tsx
│   │   │
│   │   ├── _lib/             # User dashboard utilities
│   │   │   └── server/
│   │   │       └── load-user-workspace.ts
│   │   │
│   │   ├── billing/          # Personal account billing
│   │   │   ├── _components/
│   │   │   ├── _lib/
│   │   │   │   ├── schema/
│   │   │   │   │   └── personal-account-checkout.schema.ts
│   │   │   │   └── server/
│   │   │   │       ├── personal-account-billing-page.loader.ts
│   │   │   │       ├── server-actions.ts
│   │   │   │       └── user-billing.service.ts
│   │   │   │
│   │   │   ├── error.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── page.tsx
│   │   │   └── return/
│   │   │       └── page.tsx
│   │   │
│   │   ├── settings/
│   │   │   ├── layout.tsx
│   │   │   └── page.tsx
│   │   │
│   │   ├── layout.tsx
│   │   ├── loading.tsx
│   │   └── page.tsx
│   │
│   ├── [account]/            # Team account routes (dynamic)
│   │   ├── _components/      # Team account components
│   │   │   ├── dashboard-demo.tsx
│   │   │   ├── team-account-accounts-selector.tsx
│   │   │   └── team-account-layout-sidebar.tsx
│   │   │
│   │   ├── _lib/             # Team account utilities
│   │   │   └── server/
│   │   │       ├── team-account-billing-page.loader.ts
│   │   │       └── team-account-workspace.loader.ts
│   │   │
│   │   ├── billing/          # Team billing section
│   │   │   ├── _components/
│   │   │   ├── _lib/
│   │   │   │   ├── schema/
│   │   │   │   │   └── team-billing.schema.ts
│   │   │   │   └── server/
│   │   │   │       ├── server-actions.ts
│   │   │   │       └── team-billing.service.ts
│   │   │   │
│   │   │   ├── error.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── page.tsx
│   │   │   └── return/
│   │   │       └── page.tsx
│   │   │
│   │   ├── members/          # Team members management
│   │   │   ├── _lib/
│   │   │   │   └── server/
│   │   │   │       └── members-page.loader.ts
│   │   │   └── page.tsx
│   │   │
│   │   ├── settings/
│   │   │   └── page.tsx
│   │   │
│   │   ├── layout.tsx
│   │   ├── loading.tsx
│   │   └── page.tsx
│   │
│   └── loading.tsx
│
├── join/                     # Team join page
│   └── page.tsx
│
├── update-password/
│   └── page.tsx
│
├── error.tsx                 # Global error page
├── global-error.tsx          # Global error component
├── layout.tsx                # Root layout
├── not-found.tsx             # 404 page
├── robots.ts                 # Robots.txt config
├── sitemap.xml/              # Sitemap generation
│   └── route.ts
└── version/                  # Version info endpoint
    └── route.ts
```

## Key Organization Patterns

1. **Route Groups**
    - `(marketing)` - Groups all marketing/public pages
    - `(auth)` - Groups all authentication related pages
    - `(user)` - Groups all personal user dashboard pages

2. **Component Organization**
    - `_components/` - Route-specific components
    - Global components are in the root `/components` directory (not shown)

3. **Utilities & Data**
    - `_lib/` - Route-specific utilities, types, and helpers
    - `_lib/server/` - Server-side utilities including data loaders
    - `/lib/` - Global utilities (not shown)

4. **Data Fetching**
    - Use of React's `cache()` function for request deduplication

5. **Server Actions**
    - `server-actions.ts` - Server-side actions for mutating data
    - Follows 'use server' directive pattern

6. **Special Files**
    - `layout.tsx` - Define layouts for routes
    - `loading.tsx` - Loading UI for routes
    - `error.tsx` - Error handling for routes
    - `page.tsx` - Page component for routes
    - `route.ts` - API route handlers

7. **Dynamic Routes**
    - `[account]` -  Dynamic route for team accounts. The [account] property is the account slug in the table `public.accounts`.
    - `[slug]` - Dynamic route for blog posts and documentation

# Creating Pages

# Makerkit Page & Layout Guidelines

## Page Structure Overview

Makerkit uses Next.js App Router architecture with a clear separation of concerns for layouts and pages. The application's structure reflects the multi-tenant approach with specific routing patterns:

```
- app
  - home        # protected routes
    - (user)    # user workspace (personal account context)
    - [account] # team workspace (team account context)
  - (marketing) # marketing pages
  - auth        # auth pages
```

## Key Components

### Layouts

Layouts in Makerkit provide the structure for various parts of the application:

1. **Root Layout**: The base structure for the entire application
2. **Workspace Layouts**:
    - User Workspace Layout (`app/home/(user)/layout.tsx`): For personal account context
    - Team Workspace Layout (`app/home/[account]/layout.tsx`): For team account context

Layouts handle:
- Workspace context providers
- Navigation components
- Authentication requirements
- UI structure (sidebar vs header style)

### Pages

Pages represent the actual content for each route and follow a consistent pattern:

1. **Metadata Generation**: Using `generateMetadata()` for SEO and page titles
2. **Content Structure**:
    - Page headers with titles and descriptions
    - Page body containing the main content
3. **i18n Implementation**: Wrapped with `withI18n` HOC

## Creating a New Page

### 1. Define the Page Structure

Create a new file within the appropriate route folder:

```tsx
// app/home/(user)/my-feature/page.tsx
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';

import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';

// Import components from the _components folder if needed
import { MyFeatureHeader } from './_components/my-feature-header';

export const generateMetadata = async () => {
  const i18n = await createI18nServerInstance();
  const title = i18n.t('account:myFeaturePage');

  return {
    title,
  };
};

function MyFeaturePage() {
  return (
    <>
      <MyFeatureHeader
        title={<Trans i18nKey={'common:routes.myFeature'} />}
        description={<Trans i18nKey={'common:myFeatureDescription'} />}
      />

      <PageBody>
        {/* Main page content */}
      </PageBody>
    </>
  );
}

export default withI18n(MyFeaturePage);
```

- Authentication is enforced already in the middleware
- Authorization is normally enforced by RLS at the database level
- In the rare case you use the Supabase Admin client, you must enforce both authentication and authorization manually

### 2. Create a Loading State

```tsx
// app/home/(user)/my-feature/loading.tsx
import { GlobalLoader } from '@kit/ui/global-loader';

export default GlobalLoader;
```

### 3. Create a Layout (if needed)

If the feature requires a specific layout, create a layout file:

```tsx
// app/home/(user)/my-feature/layout.tsx
import { use } from 'react';

import { UserWorkspaceContextProvider } from '@kit/accounts/components';
import { Page, PageNavigation } from '@kit/ui/page';

import { withI18n } from '~/lib/i18n/with-i18n';
import { loadUserWorkspace } from '../_lib/server/load-user-workspace';

// Import components from the _components folder
import { MyFeatureNavigation } from './_components/my-feature-navigation';

function MyFeatureLayout({ children }: React.PropsWithChildren) {
  const workspace = use(loadUserWorkspace());

  return (
    <UserWorkspaceContextProvider value={workspace}>
      <Page>
        <PageNavigation>
          <MyFeatureNavigation workspace={workspace} />
        </PageNavigation>

        {children}
      </Page>
    </UserWorkspaceContextProvider>
  );
}

export default withI18n(MyFeatureLayout);
```

## Layout Patterns

### 1. User Workspace Layout

For pages in the personal account context, use the user workspace layout pattern:

```tsx
import { use } from 'react';

import { UserWorkspaceContextProvider } from '@kit/accounts/components';
import { Page } from '@kit/ui/page';
import { withI18n } from '~/lib/i18n/with-i18n';
import { loadUserWorkspace } from './_lib/server/load-user-workspace';

function MyLayout({ children }: React.PropsWithChildren) {
  const workspace = use(loadUserWorkspace());

  return (
    <UserWorkspaceContextProvider value={workspace}>
      <Page>
        {/* Navigation components */}
        {children}
      </Page>
    </UserWorkspaceContextProvider>
  );
}

export default withI18n(MyLayout);
```

### 2. Team Workspace Layout

For pages in the team account context, use the team workspace layout pattern:

```tsx
import { use } from 'react';

import { TeamAccountWorkspaceContextProvider } from '@kit/team-accounts/components';
import { Page } from '@kit/ui/page';
import { withI18n } from '~/lib/i18n/with-i18n';
import { loadTeamWorkspace } from './_lib/server/load-team-workspace';

function TeamLayout({ children, params }: LayoutParams) {
  const workspace = use(loadTeamWorkspace(params.account));

  return (
    <TeamAccountWorkspaceContextProvider value={workspace}>
      <Page>
        {/* Navigation components */}
        {children}
      </Page>
    </TeamAccountWorkspaceContextProvider>
  );
}

export default withI18n(TeamLayout);
```

## UI Components Structure

### Page Components

Break down pages into reusable components:

1. **Page Headers**: Create header components for consistent titling:
   ```tsx
   // _components/my-feature-header.tsx
   import { PageHeader } from '@kit/ui/page-header';

   export function MyFeatureHeader({
     title,
     description
   }: {
     title: React.ReactNode,
     description: React.ReactNode
   }) {
     return (
       <PageHeader
         title={title}
         description={description}
       />
     );
   }
   ```

2. **Feature Components**: Create components for feature-specific functionality:
   ```tsx
   // _components/my-feature-component.tsx
   'use client';

   import { useUserWorkspace } from '@kit/accounts/hooks/use-user-workspace';

   export function MyFeatureComponent() {
     const { user } = useUserWorkspace();

     return (
       <div>
         {/* Component content */}
       </div>
     );
   }
   ```

### Navigation Components

Create navigation components to handle sidebar or header navigation:

```tsx
// _components/my-feature-navigation.tsx
'use client';

import { NavigationMenu } from '@kit/ui/navigation-menu';

export function MyFeatureNavigation({
  workspace
}: {
  workspace: UserWorkspace
}) {
  return (
    <NavigationMenu>
      {/* Navigation items */}
    </NavigationMenu>
  );
}
```

## Layout Styles

Makerkit supports different layout styles that can be toggled by the user:

1. **Sidebar Layout**: A vertical sidebar navigation
2. **Header Layout**: A horizontal header navigation

The layout style is stored in cookies and can be accessed server-side:

```tsx
async function getLayoutState() {
  const cookieStore = await cookies();
  const layoutStyleCookie = cookieStore.get('layout-style');

  return {
    style: layoutStyleCookie?.value ?? defaultStyle,
    // Other layout state properties
  };
}
```

## Best Practices

1. **Server vs. Client Components**:
    - Use Server Components for data fetching and initial rendering
    - Use Client Components ('use client') for interactive elements

2. **Data Loading**:
    - Load workspace data in layouts using server functions
    - Pass data down to components that need it
    - Use React Query for client-side data fetching

3. **Component Organization**:
    - Place feature-specific components in a `_components` folder
    - Place feature-specific server utilities in a `_lib/server` folder
    - Place feature-specific client utilities in a `_lib/client` folder

4. **i18n Support**:
    - Always use `withI18n` HOC for pages and layouts
    - Use `<Trans>` component for translated text
    - Define translation keys in the appropriate namespace in `apps/web/public/locales/<locale>/<namespace>.json`

5. **Metadata**:
    - Always include `generateMetadata` for SEO and UX
    - Use translations for page titles and descriptions

6. **Loading States**:
    - Always provide a loading state for each route
    - Use the `GlobalLoader` or custom loading components

7. **Error Handling**:
    - Implement error.tsx files for route error boundaries
    - Handle data fetching errors gracefully


# UI Components

- Reusable UI components are defined in the "packages/ui" package named "@kit/ui".
- By exporting the component from the "exports" field, we can import it using the "@kit/ui/{component-name}" format.

## Styling
- Styling is done using Tailwind CSS. We use the "cn" function from the "@kit/ui/utils" package to generate class names.
- Avoid fixes classes such as "bg-gray-500". Instead, use Shadcn classes such as "bg-background", "text-secondary-foreground", "text-muted-foreground", etc.

Makerkit leverages two sets of UI components:
1. **Shadcn UI Components**: Base components from the Shadcn UI library
2. **Makerkit-specific Components**: Custom components built on top of Shadcn UI

## Importing Components

```tsx
// Import Shadcn UI components
import { Button } from '@kit/ui/button';
import { Card } from '@kit/ui/card';
import { toast } from '@kit/ui/sonner';

// Import Makerkit-specific components
import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
import { ProfileAvatar } from '@kit/ui/profile-avatar';
```

## Core Shadcn UI Components

| Component | Description | Import Path |
|-----------|-------------|-------------|
| `Accordion` | Expandable/collapsible content sections | `@kit/ui/accordion` [accordion.tsx](mdc:packages/ui/src/shadcn/accordion.tsx) |
| `AlertDialog` | Modal dialog for important actions | `@kit/ui/alert-dialog` [alert-dialog.tsx](mdc:packages/ui/src/shadcn/alert-dialog.tsx) |
| `Alert` | Status/notification messages | `@kit/ui/alert` [alert.tsx](mdc:packages/ui/src/shadcn/alert.tsx) |
| `Avatar` | User profile images with fallback | `@kit/ui/avatar` [avatar.tsx](mdc:packages/ui/src/shadcn/avatar.tsx) |
| `Badge` | Small status indicators | `@kit/ui/badge` [badge.tsx](mdc:packages/ui/src/shadcn/badge.tsx) |
| `Breadcrumb` | Navigation path indicators | `@kit/ui/breadcrumb` [breadcrumb.tsx](mdc:packages/ui/src/shadcn/breadcrumb.tsx) |
| `Button` | Clickable action elements | `@kit/ui/button` [button.tsx](mdc:packages/ui/src/shadcn/button.tsx) |
| `Calendar` | Date picker and date display | `@kit/ui/calendar` [calendar.tsx](mdc:packages/ui/src/shadcn/calendar.tsx) |
| `Card` | Container for grouped content | `@kit/ui/card` [card.tsx](mdc:packages/ui/src/shadcn/card.tsx) |
| `Checkbox` | Selection input | `@kit/ui/checkbox` [checkbox.tsx](mdc:packages/ui/src/shadcn/checkbox.tsx) |
| `Command` | Command palette interface | `@kit/ui/command` [command.tsx](mdc:packages/ui/src/shadcn/command.tsx) |
| `DataTable` | Table | `@kit/ui/data-table` [data-table.tsx](mdc:packages/ui/src/shadcn/data-table.tsx) |
| `Dialog` | Modal window for focused interactions | `@kit/ui/dialog` [dialog.tsx](mdc:packages/ui/src/shadcn/dialog.tsx) |
| `DropdownMenu` | Menu triggered by a button | `@kit/ui/dropdown-menu` [dropdown-menu.tsx](mdc:packages/ui/src/shadcn/dropdown-menu.tsx) |
| `Form` | Form components with validation | `@kit/ui/form` [form.tsx](mdc:packages/ui/src/shadcn/form.tsx) |
| `Input` | Text input field | `@kit/ui/input` [input.tsx](mdc:packages/ui/src/shadcn/input.tsx) |
| `Input OTP` | OTP Text input field | `@kit/ui/input-otp` [input-otp.tsx](mdc:packages/ui/src/shadcn/input-otp.tsx) |
| `Label` | Text label for form elements | `@kit/ui/label` [label.tsx](mdc:packages/ui/src/shadcn/label.tsx) |
| `NavigationMenu` | Hierarchical navigation component | `@kit/ui/navigation-menu` [navigation-menu.tsx](mdc:packages/ui/src/shadcn/navigation-menu.tsx) |
| `Popover` | Floating content triggered by interaction | `@kit/ui/popover` [popover.tsx](mdc:packages/ui/src/shadcn/popover.tsx) |
| `RadioGroup` | Radio button selection group | `@kit/ui/radio-group` [radio-group.tsx](mdc:packages/ui/src/shadcn/radio-group.tsx) |
| `ScrollArea` | Customizable scrollable area | `@kit/ui/scroll-area` [scroll-area.tsx](mdc:packages/ui/src/shadcn/scroll-area.tsx) |
| `Select` | Dropdown selection menu | `@kit/ui/select` [select.tsx](mdc:packages/ui/src/shadcn/select.tsx) |
| `Separator` | Visual divider between content | `@kit/ui/separator` [separator.tsx](mdc:packages/ui/src/shadcn/separator.tsx) |
| `Sheet` | Sliding panel from screen edge | `@kit/ui/sheet` [sheet.tsx](mdc:packages/ui/src/shadcn/sheet.tsx) |
| `Sidebar` | Advanced sidebar navigation | `@kit/ui/shadcn-sidebar` [sidebar.tsx](mdc:packages/ui/src/shadcn/sidebar.tsx) |
| `Skeleton` | Loading placeholder | `@kit/ui/skeleton` [skeleton.tsx](mdc:packages/ui/src/shadcn/skeleton.tsx) |
| `Switch` | Toggle control | `@kit/ui/switch` [switch.tsx](mdc:packages/ui/src/shadcn/switch.tsx) |
| `Toast` | Toaster | `@kit/ui/sonner` [sonner.tsx](mdc:packages/ui/src/shadcn/sonner.tsx) |
| `Tabs` | Tab-based navigation | `@kit/ui/tabs` [tabs.tsx](mdc:packages/ui/src/shadcn/tabs.tsx) |
| `Textarea` | Multi-line text input | `@kit/ui/textarea` [textarea.tsx](mdc:packages/ui/src/shadcn/textarea.tsx) |
| `Tooltip` | Contextual information on hover | `@kit/ui/tooltip` [tooltip.tsx](mdc:packages/ui/src/shadcn/tooltip.tsx) |

## Makerkit-specific Components

| Component | Description | Import Path |
|-----------|-------------|-------------|
| `If` | Conditional rendering component | `@kit/ui/if` [if.tsx](mdc:packages/ui/src/makerkit/if.tsx) |
| `Trans` | Internationalization text component | `@kit/ui/trans` [trans.tsx](mdc:packages/ui/src/makerkit/trans.tsx) |
| `Page` | Page layout with navigation | `@kit/ui/page` [page.tsx](mdc:packages/ui/src/makerkit/page.tsx) |
| `GlobalLoader` | Full-page loading indicator | `@kit/ui/global-loader` [global-loader.tsx](mdc:packages/ui/src/makerkit/global-loader.tsx) |
| `ImageUploader` | Image upload component | `@kit/ui/image-uploader` [image-uploader.tsx](mdc:packages/ui/src/makerkit/image-uploader.tsx) |
| `ProfileAvatar` | User avatar with fallback | `@kit/ui/profile-avatar` [profile-avatar.tsx](mdc:packages/ui/src/makerkit/profile-avatar.tsx) |
| `DataTable` (Enhanced) | Extended data table with pagination | `@kit/ui/enhanced-data-table` [data-table.tsx](mdc:packages/ui/src/makerkit/data-table.tsx) |
| `Stepper` | Multi-step process indicator | `@kit/ui/stepper` [stepper.tsx](mdc:packages/ui/src/makerkit/stepper.tsx) |
| `CookieBanner` | GDPR-compliant cookie notice | `@kit/ui/cookie-banner` [cookie-banner.tsx](mdc:packages/ui/src/makerkit/cookie-banner.tsx) |
| `CardButton` | Card-styled button | `@kit/ui/card-button` [card-button.tsx](mdc:packages/ui/src/makerkit/card-button.tsx) |
| `MultiStepForm` | Form with multiple steps | `@kit/ui/multi-step-form` [multi-step-form.tsx](mdc:packages/ui/src/makerkit/multi-step-form.tsx) |
| `EmptyState` | Empty data placeholder | `@kit/ui/empty-state` [empty-state.tsx](mdc:packages/ui/src/makerkit/empty-state.tsx) |
| `AppBreadcrumbs` | Application path breadcrumbs | `@kit/ui/app-breadcrumbs` [app-breadcrumbs.tsx](mdc:packages/ui/src/makerkit/app-breadcrumbs.tsx) |

## Marketing Components

Import all marketing components with:
```tsx
import {
  Hero,
  HeroTitle,
  GradientText,
  // etc.
} from '@kit/ui/marketing';
```

Key marketing components:
- `Hero` - Hero sections [hero.tsx](mdc:packages/ui/src/makerkit/marketing/hero.tsx)
- `SecondaryHero` [secondary-hero.tsx](mdc:packages/ui/src/makerkit/marketing/secondary-hero.tsx)
- `FeatureCard`, `FeatureGrid` - Feature showcases [feature-card.tsx](mdc:packages/ui/src/makerkit/marketing/feature-card.tsx)
- `Footer` - Page Footer [footer.tsx](mdc:packages/ui/src/makerkit/marketing/footer.tsx)
- `Header` - Page Header [header.tsx](mdc:packages/ui/src/makerkit/marketing/header.tsx)
- `NewsletterSignup` - Email collection [newsletter-signup-container.tsx](mdc:packages/ui/src/makerkit/marketing/newsletter-signup-container.tsx)
- `ComingSoon` - Coming soon page template [coming-soon.tsx](mdc:packages/ui/src/makerkit/marketing/coming-soon.tsx)


# Forms

- Use React Hook Form for form validation and submission.
- Use Zod for form validation.
- Use the `zodResolver` function to resolve the Zod schema to the form.
- Use Server Actions [server-actions.mdc](mdc:.cursor/rules/server-actions.mdc) for server-side code handling
- Use Sonner for writing toasters for UI feedback

Follow the example below to create all forms:

## Define the schema

Zod schemas should be defined in the `schema` folder and exported, so we can reuse them across a Server Action and the client-side form:

```tsx
// _lib/schema/create-note.schema.ts
import { z } from 'zod';

export const CreateNoteSchema = z.object({
  title: z.string().min(1),
  content: z.string().min(1),
});
```

## Create the Server Action

Server Actions [server-actions.mdc](mdc:.cursor/rules/server-actions.mdc) can help us create endpoints for our forms.

```tsx
'use server';

import { z } from 'zod';
import { enhanceAction } from '@kit/next/actions';
import { CreateNoteSchema } from '../schema/create-note.schema';

export const createNoteAction = enhanceAction(
  async function (data, user) {
    // 1. "data" has been validated against the Zod schema, and it's safe to use
    // 2. "user" is the authenticated user

    // ... your code here
    return {
      success: true,
    };
  },
  {
    auth: true,
    schema: CreateNoteSchema,
  },
);
```

## Create the Form Component

Then create a client component to handle the form submission:

```tsx
'use client';

import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { Textarea } from '@kit/ui/textarea';
import { Input } from '@kit/ui/input';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@kit/ui/form';
import { toast } from '@kit/ui/sonner';
import { useTranslation } from 'react-i18next';

import { CreateNoteSchema } from '../_lib/schema/create-note.schema';

export function CreateNoteForm() {
  const [pending, startTransition] = useTransition();
  const { t } = useTranslation();

  const form = useForm({
    resolver: zodResolver(CreateNoteSchema),
    defaultValues: {
      title: '',
      content: '',
    },
  });

  const onSubmit = (data) => {
    startTransition(async () => {
      await toast.promise(createNoteAction(data), {
        loading: t('notes:creatingNote`),
        success: t('notes:createNoteSuccess`),
        error: t('notes:createNoteError`)
      })
    });
  };

  return (
    <form onSubmit={form.handleSubmit(onSubmit)}>
      <Form {...form}>
        <FormField name={'title'} render={({ field }) => (
          <FormItem>
            <FormLabel>
              <span className={'text-sm font-medium'}>Title</span>
            </FormLabel>

            <FormControl>
              <Input
                type={'text'}
                className={'w-full'}
                placeholder={'Title'}
                {...field}
              />
            </FormControl>

            <FormMessage />
          </FormItem>
        )} />

        <FormField name={'content'} render={({ field }) => (
          <FormItem>
            <FormLabel>
              <span className={'text-sm font-medium'}>Content</span>
            </FormLabel>

            <FormControl>
              <Textarea
                className={'w-full'}
                placeholder={'Content'}
                {...field}
              />
            </FormControl>

            <FormMessage />
          </FormItem>
        )} />

        <button disabled={pending} type={'submit'} className={'w-full'}>
          Submit
        </button>
      </Form>
    </form>
  );
}
```

Always use `@kit/ui` for writing the UI of the form.


# Data Fetching

## General Data Flow
- In a Server Component context, please use the Supabase Client directly for data fetching
- In a Client Component context, please use the `useQuery` hook from the "@tanstack/react-query" package

Data Flow works in the following way:

1. Server Component uses the Supabase Client to fetch data.
2. Data is rendered in Server Components or passed down to Client Components when absolutely necessary to use a client component (e.g. when using React Hooks or any interaction with the DOM).

```tsx
import { getSupabaseServerClient } from '@kit/supabase/server-client';

async function ServerComponent() {
  const client = getSupabaseServerClient();
  const { data, error } = await client.from('notes').select('*');

  // use data
}
```

or pass down the data to a Client Component:

```tsx
import { getSupabaseServerClient } from '@kit/supabase/server-client';

export default function ServerComponent() {
  const supabase = getSupabaseServerClient();
  const { data, error } = await supabase.from('notes').select('*');

  if (error) {
    return <SomeErrorComponent error={error} />;
  }

  return <SomeClientComponent data={data} />;
}
```

## Supabase Clients
- In a Server Component context, use the `getSupabaseServerClient` function from the "@kit/supabase/server-client" package [server-client.ts](mdc:packages/supabase/src/clients/server-client.ts)
- In a Client Component context, use the `useSupabase` hook from the "@kit/supabase/hooks/use-supabase" package.

### Admin Actions

Only in rare cases suggest using the Admin client `getSupabaseServerAdminClient` when needing to bypass RLS from the package `@kit/supabase/server-admin-client` [server-admin-client.ts](mdc:packages/supabase/src/clients/server-admin-client.ts)

## React Query

When using `useQuery`, make sure to define the data fetching hook. Create two components: one that fetches the data and one that displays the data. For example a good usage is [roles-data-provider.tsx](mdc:packages/features/team-accounts/src/components/members/roles-data-provider.tsx) as shown in [update-member-role-dialog.tsx](mdc:packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx)

## Error Handling

- Logging using the `@kit/shared/logger` package [logger.ts](mdc:packages/shared/src/logger/logger.ts)
- Don't swallow errors, always handle them appropriately
- Handle promises and async/await gracefully
- Consider the unhappy path and handle errors appropriately
- Context without sensitive data

```tsx
'use server';

import { getLogger } from '@kit/shared/logger';

export async function myServerAction() {
  const logger = await getLogger();

  logger.info('Request started...');

  try {
    // your code here
    await someAsyncFunction();

    logger.info('Request succeeded...');
  } catch (error) {
    logger.error('Request failed...');

    // handle error
  }

  return {
    success: true,
  };
}
```

# Database Rules

## Database Architecture
- Supabase uses Postgres
- We strive to create a safe, robust, performant schema
- Accounts are the general concept of a user account, defined by the having the same ID as Supabase Auth's users (personal). They can be a team account or a personal account.
- Generally speaking, other tables will be used to store data related to the account. For example, a table `notes` would have a foreign key `account_id` to link it to an account.

## Schemas
- The DB schemas are available at `apps/web/supabase/schemas`
- To edit the DB schema, we can either change the schema files, or created new ones
- To create a new schema, create a file at `apps/web/supabase/schemas/<number>-<name>.sql`

## Migrations
- After creating a schema, we can create a migration
- Use the command `pnpm --filter web supabase:db:diff` for creating migrations from schemas
- After generating a migration, reset the database for applying the changes using the command `pnpm --filter web supabase:db:reset`

## Security & RLS
- Using RLS, we must ensure that only the account owner can access the data. Always write safe RLS policies and ensure that the policies are enforced.
- Unless specified, always enable RLS when creating a table. Propose the required RLS policies ensuring the safety of the data.
- Always consider any required constraints and triggers are in place for data consistency
- Always consider the compromises you need to make and explain them so I can make an educated decision. Follow up with the considerations make and explain them.
- Always consider the security of the data and explain the security implications of the data.
- Always use Postgres schemas explicitly (e.g., `public.accounts`)
- Consider the required compromises between simplicity, functionality and developer experience. However, never compromise on security, which is paramount and fundamental.
- Use existing helper functions for access control instead of making your own queries, unless unavailable

## Schema Overview

Makerkit uses a Supabase Postgres database with a well-defined schema focused on multi-tenancy through the concepts of accounts (both personal and team) and robust permission systems.

### Database Schema

1. Enums [01-enums.sql](mdc:apps/web/supabase/schemas/01-enums.sql)
2. Config [02-config.sql](mdc:apps/web/supabase/schemas/02-config.sql)
3. Accounts [03-accounts.sql](mdc:apps/web/supabase/schemas/03-accounts.sql)
4. Roles [04-roles.sql](mdc:apps/web/supabase/schemas/04-roles.sql)
5. Memberships [05-memberships.sql](mdc:apps/web/supabase/schemas/05-memberships.sql)
6. Roles Permissions [06-roles-permissions.sql](mdc:apps/web/supabase/schemas/06-roles-permissions.sql)
7. Invitations [07-invitations.sql](mdc:apps/web/supabase/schemas/07-invitations.sql)
8. Billing Customers [08-billing-customers.sql](mdc:apps/web/supabase/schemas/08-billing-customers.sql)
9. Subscriptions [09-subscriptions.sql](mdc:apps/web/supabase/schemas/09-subscriptions.sql)
10. Orders [10-orders.sql](mdc:apps/web/supabase/schemas/10-orders.sql)
11. Notifications [11-notifications.sql](mdc:apps/web/supabase/schemas/11-notifications.sql)
12. One Time Tokens [12-one-time-tokens.sql](mdc:apps/web/supabase/schemas/12-one-time-tokens.sql)
13. Multi Factor Auth [13-mfa.sql](mdc:apps/web/supabase/schemas/13-mfa.sql)
14. Super Admin [14-super-admin.sql](mdc:apps/web/supabase/schemas/14-super-admin.sql)
15. Account Views [15-account-views.sql](mdc:apps/web/supabase/schemas/15-account-views.sql)
16. Storage [16-storage.sql](mdc:apps/web/supabase/schemas/16-storage.sql)

## Database Best Practices

### Inferring Database types

Fetch auto-generated data types using the `@kit/supabase/database` import. Do not write types manually if the shape is the same as the one from the database row.

```tsx
import { Tables } from '@kit/supabase/database';

// public.accounts
type Account = Tables<'accounts'>;

// public.subscriptions
type Subscription = Tables<'subscriptions'>;

// public.notifications
type Notification = Tables<'notifications'>;

// ...
```

### Security

- **Always enable RLS** on new tables unless explicitly instructed otherwise
- **Create proper RLS policies** for all CRUD operations following existing patterns
- **Always associate data with accounts** using a foreign key to ensure proper access control
- **Use explicit schema references** (`public.table_name` not just `table_name`)
- **Private schema**: Place internal functions in the `kit` schema
- **Search Path**: Always set search path to '' when defining functions
- **Security Definer**: Do not use `security definer` functions unless stricly required

### Data Access Patterns

- Use `public.has_role_on_account(account_id, role?)` to check membership
- Use `public.has_permission(user_id, account_id, permission)` for permission checks
- Use `public.is_account_owner(account_id)` to identify account ownership

### SQL Coding Style

- Use explicit transactions for multi-step operations
- Follow existing naming conventions:
    - Tables: snake_case, plural nouns (`accounts`, `subscriptions`)
    - Functions: snake_case, verb phrases (`create_team_account`, `verify_nonce`)
    - Triggers: descriptive action names (`set_slug_from_account_name`)
- Document functions and complex SQL with comments
- Use parameterized queries to prevent SQL injection

### Common Patterns

- **Account Lookup**: Typically by `id` (UUID) or `slug` (for team accounts)
- **Permission Check**: Always verify proper permissions before mutations
- **Timestamp Automation**: Use the `trigger_set_timestamps()` function
- **User Tracking**: Use the `trigger_set_user_tracking()` function
- **Configuration**: Use `is_set(field_name)` to check enabled features

## Best Practices for Database Code

### 1. RLS Policy Management

#### Always Enable RLS

Always enable RLS for your tables unless you have a specific reason not to.
  ```sql
  ALTER TABLE public.my_table ENABLE ROW LEVEL SECURITY;
  ```

#### Use Helper Functions to validate permissions and access control
Use the existing structure for policies:
  ```sql
  -- SELECT policy
  CREATE POLICY "my_table_read" ON public.my_table FOR SELECT
    TO authenticated USING (
      account_id = (select auth.uid()) OR
      public.has_role_on_account(account_id)
    );

  -- INSERT/UPDATE/DELETE policies follow similar patterns
  ```

When using RLS at team-account level, use `public.has_role_on_account(account_id)` for a generic check to understand if a user is part of a team.

When using RLS at user-account level, use `account_id = (select auth.uid())`.

When an entity can belong to both, use both.

When requiring a specific role, use the role parameter `public.has_role_on_account(account_id, 'owner')`

### 2. Account Association

- **Associate Data with Accounts**: Always link data to accounts using a foreign key:
  ```sql
  CREATE TABLE if not exists public.my_data (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    account_id UUID REFERENCES public.accounts(id) ON DELETE CASCADE NOT NULL,
    /* other fields */
  );
  ```

### 3. Permission System

- **Use the Permission System**: Leverage the built-in permission system for access control:
  ```sql
  -- Check if a user has a specific permission
  SELECT public.has_permission(
    auth.uid(),
    account_id,
    'my.permission'::public.app_permissions
  );
  ```

### 4. Schema Organization

- **Use Schemas Explicitly**: Always use schema prefixes explicitly:
  ```sql
  -- Good
  SELECT * FROM public.accounts;

  -- Avoid
  SELECT * FROM accounts;
  ```

- **Put Internal Functions in 'kit' Schema**: Use the 'kit' schema for internal helper functions
  ```sql
  CREATE OR REPLACE FUNCTION kit.my_helper_function()
  RETURNS void AS $$
  -- function body
  $$ LANGUAGE plpgsql;
  ```

### 5. Types and Constraints

- **Use Enums for Constrained Values**: Create and use enum types for values with a fixed set:
  ```sql
  CREATE TYPE public.my_status AS ENUM('active', 'inactive', 'pending');

  CREATE TABLE if not exists public.my_table (
    status public.my_status NOT NULL DEFAULT 'pending'
  );
  ```

- **Apply Appropriate Constraints**: Use constraints to ensure data integrity:
  ```sql
  CREATE TABLE if not exists public.my_table (
    email VARCHAR(255) NOT NULL CHECK (email ~* '^.+@.+\..+$'),
    count INTEGER NOT NULL CHECK (count >= 0),
    /* other fields */
  );
  ```

### 6. Authentication and User Management

- **Use Supabase Auth**: Leverage auth.users for identity management
- **Handle User Creation**: Use triggers like `kit.setup_new_user` to set up user data after registration

### 7. Function Security

- **Apply Security Definer Carefully**: For functions that need elevated privileges:
  ```sql
  CREATE OR REPLACE FUNCTION public.my_function()
  RETURNS void
  LANGUAGE plpgsql
  SECURITY DEFINER
  SET search_path = '' AS $$
  -- function body
  $$;
  ```

- **Set Proper Function Permissions**:
  ```sql
  GRANT EXECUTE ON FUNCTION public.my_function() TO authenticated, service_role;
  ```

### 8. Error Handling and Validation

- **Use Custom Error Messages**: Return meaningful errors:
  ```sql
  IF NOT validation_passed THEN
    RAISE EXCEPTION 'Validation failed: %', error_message;
  END IF;
  ```

### 9. Triggers for Automation

- **Use Triggers for Derived Data**: Automate updates to derived fields:
  ```sql
  CREATE TRIGGER update_timestamp
  BEFORE UPDATE ON public.my_table
  FOR EACH ROW EXECUTE FUNCTION public.trigger_set_timestamps();
  ```

### 10. View Structure for Commonly Used Queries

- **Create Views for Complex Joins**: As done with `user_account_workspace`
  ```sql
  CREATE OR REPLACE VIEW public.my_view
  WITH (security_invoker = true) AS
  SELECT ...
  ```

You always must use `(security_invoker = true)` for views.

## Key Functions to Know

1. **Account Access**
    - `public.has_role_on_account(account_id, account_role)`
    - `public.is_account_owner(account_id)`
    - `public.is_team_member(account_id, user_id)`

2. **Permissions**
    - `public.has_permission(user_id, account_id, permission_name)`
    - `public.has_more_elevated_role(target_user_id, target_account_id, role_name)`

3. **Team Management**
    - `public.create_team_account(account_name)`

4. **Billing & Subscriptions**
    - `public.has_active_subscription(target_account_id)`

5. **One-Time Tokens**
    - `public.create_nonce(...)`
    - `public.verify_nonce(...)`
    - `public.revoke_nonce(...)`

6. **Super Admins**
    - `public.is_super_admin()`

7. **MFA**:
    - `public.is_aal2()`
    - `public.is_mfa_compliant()`

## Configuration Control

- **Use the `config` Table**: The application has a central configuration table
- **Check Features with `public.is_set(field_name)`**:
  ```sql
  -- Check if team accounts are enabled
  SELECT public.is_set('enable_team_accounts');
  ```

# Server Actions

- For Data Mutations from Client Components, always use Server Actions
- Always name the server actions file as "server-actions.ts"
- Always name exported Server Actions suffixed as "Action", ex. "createPostAction"
- Always use the `enhanceAction` function from the "@kit/supabase/actions" package [index.ts](mdc:packages/next/src/actions/index.ts)
- Always use the 'use server' directive at the top of the file
- Place the Zod schema in a separate file so it can be reused with `react-hook-form`

```tsx
'use server';

import { z } from 'zod';
import { enhanceAction } from '@kit/next/actions';
import { EntitySchema } from '../entity.schema.ts`;

export const myServerAction = enhanceAction(
  async function (data, user) {
    // 1. "data" is already a valid EntitySchema and it's safe to use
    // 2. "user" is the authenticated user

    // ... your code here
    return {
      success: true,
    };
  },
  {
    auth: true,
    schema: EntitySchema,
  },
);
```

# Route Handler / API Routes

- Use Route Handlers when data fetching from Client Components
- To create API routes (route.ts), always use the `enhanceRouteHandler` function from the "@kit/supabase/routes" package. [index.ts](mdc:packages/next/src/routes/index.ts)

```tsx
import { z } from 'zod';
import { enhanceRouteHandler } from '@kit/next/routes';
import { NextResponse } from 'next/server';

const ZodSchema = z.object({
  email: z.string().email(),
  password: z.string().min(6),
});

export const POST = enhanceRouteHandler(
  async function({ body, user, request }) {
    // 1. "body" is already a valid ZodSchema and it's safe to use
    // 2. "user" is the authenticated user
    // 3. "request" is NextRequest
    // ... your code here
    return NextResponse.json({
      success: true,
    });
  },
  {
    schema: ZodSchema,
  },
);

// example of unauthenticated route (careful!)
export const GET = enhanceRouteHandler(
  async function({ user, request }) {
    // 1. "user" is null, as "auth" is false and we don't require authentication
    // 2. "request" is NextRequest
    // ... your code here
    return NextResponse.json({
      success: true,
    });
  },
  {
    auth: false,
  },
);
```

# Access Control & Permissions Guidelines

This rule provides guidance for implementing access control, permissions, and subscription-related functionality in the application.

## Role-Based Access Control

### Account Roles
- Roles are defined in the `roles` table with hierarchy levels (lower number = higher privilege)
- Default roles include `owner` (hierarchy_level=1) and `member` with specific permissions
- Primary account owner has special privileges that cannot be revoked
- Role hierarchy controls what actions users can perform on other members

### Role Permissions
- Permissions are stored in `role_permissions` table mapping roles to specific permissions
- Core permissions:
    - `roles.manage`: Manage roles of users with lower hierarchy
    - `billing.manage`: Access/update billing information
    - `settings.manage`: Update account settings
    - `members.manage`: Add/remove members
    - `invites.manage`: Create/update/delete invitations

### Permission Checking
- Use `has_permission(user_id, account_id, permission_name)` to check specific permissions
- Use `can_action_account_member(target_team_account_id, target_user_id)` to verify if a user can act on another
- Use `is_account_owner(account_id)` to check if user is primary owner
- Primary owners can perform any action regardless of explicit permissions

## Team Account Access

### Team Membership
- Use `has_role_on_account(account_id, account_role)` to check if user is a member with specific role
- Use `is_team_member(account_id, user_id)` to check if a specific user is a member
- Use the authenticated user's `TeamAccountWorkspaceContext` to access current permissions array

### Invitations
- Only users with `invites.manage` permission can create/manage invitations
- Users can only invite others with the same or lower role hierarchy than they have
- Invitations have expiry dates (default: 7 days)
- Accept invitations using `accept_invitation` function with token

## Subscription Access

### Subscription Status Checking
- Check active subscriptions with `has_active_subscription(account_id)`
- Active status includes both `active` and `trialing` subscriptions
- Guard premium features with subscription checks in both frontend and backend

### Billing Access
- Only users with `billing.manage` permission can access billing functions
- All billing operations should be guarded with permission checks
- Per-seat billing automatically updates when members are added/removed

## Row Level Security

### Table RLS
- Most tables have RLS policies restricting access based on team membership
- Personal account data is only accessible by the account owner
- Team account data is accessible by all team members based on their roles

### Actions on Members
- Higher roles can update/remove lower roles but not equal or higher roles
- Primary owner cannot be removed from their account
- Ownership transfer requires OTP verification and is limited to primary owners


# i18n System Guide

This document provides a comprehensive overview of the internationalization (i18n) system in our Next.js application.

## Architecture Overview

The i18n system consists of:

1. **Core i18n Package**: Located in `packages/i18n`, providing the foundation for i18n functionality
2. **Application-specific Implementation**: Located in `apps/web/lib/i18n`, customizing the core functionality
3. **Translation Files**: Located in `apps/web/public/locales/[language]/[namespace].json`

## Usage Guide

### 1. Setting Up a Page or Layout with i18n

Wrap your page or layout component with the `withI18n` HOC:

```typescript
import { withI18n } from '~/lib/i18n/with-i18n';

function HomePage() {
  // Your component code
}

export default withI18n(HomePage);
```

### 2. Using Translations in Client Components

Use the `useTranslation` hook from react-i18next:

```tsx
'use client';

import { useTranslation } from 'react-i18next';

export function MyComponent() {
  const { t } = useTranslation('common');
  
  return <h1>{t('homeTabLabel')}</h1>;
}
```

### 3. Using Translations with the Trans Component

For complex translations that include HTML or variables:

```tsx
import { Trans } from '@kit/ui/trans';

export function MyComponent() {
  return (
    <div>
      <Trans 
        i18nKey="teams:inviteAlertBody" 
        values={{ accountName: 'My Team' }}
      />
    </div>
  );
}
```

### 4. Adding Language Selection to Your UI

Use the `LanguageSelector` component:

```tsx
import { LanguageSelector } from '@kit/ui/language-selector';

export function SettingsPage() {
  return (
    <div>
      <h2>Language Settings</h2>
      <LanguageSelector />
    </div>
  );
}
```

### 5. Adding New Translations

1. Create or update JSON files in `apps/web/public/locales/[language]/[namespace].json`
2. Follow the existing structure, adding your new keys

For example, in `apps/web/public/locales/en/common.json`:
```json
{
  "existingKey": "Existing translation",
  "newKey": "New translation text"
}
```

### 6. Adding a New Language

1. Add the language code to the `languages` array in `i18n.settings.ts`
2. Create corresponding translation files in `apps/web/public/locales/[new-language]/`
3. Copy the structure from the English files as a template

### 7. Adding a New Namespace

1. Add the namespace to `defaultI18nNamespaces` in `i18n.settings.ts`
2. Create corresponding translation files for all supported languages

## Advanced Usage

### Dynamic Namespace Loading

When you need translations from namespaces not included in the default set:

```typescript
import { getI18nSettings } from '~/lib/i18n/i18n.settings';

// Load specific namespaces
const settings = getI18nSettings(language, ['specific-namespace']);
```

### Language Priority

The system uses the following priority to determine the language:
1. User-selected language (from cookie)
2. Browser language (if priority is set to 'user')
3. Default language from environment variable

### Common Issues

- **Translation not showing**: Check that you're using the correct namespace
- **Dynamic content not interpolated**: Make sure to use the `values` prop with `Trans` component

## Available Namespaces and Keys

Here's a brief overview of the available namespaces:

- **common**: General UI elements, navigation, errors [common.json](mdc:apps/web/public/locales/en/common.json)
- **auth**: Authentication-related text [auth.json](mdc:apps/web/public/locales/en/auth.json)
- **account**: Account settings and profile [account.json](mdc:apps/web/public/locales/en/account.json)
- **teams**: Team management [teams.json](mdc:apps/web/public/locales/en/teams.json)
- **billing**: Subscription and payment [billing.json](mdc:apps/web/public/locales/en/billing.json)
- **marketing**: Landing pages, blog, etc. [marketing.json](mdc:apps/web/public/locales/en/marketing.json)

When creating a new functionality, it can be useful to add a new namespace.

# Security Best Practices

## Next.js-Specific Security

### Client Component Data Passing

- **Never pass sensitive data** to Client Components
- **Never pass unsanitized data** to Client Components (raw cookies, client-provided data)

### Server Components Security

- **Always sanitize user input** before using in Server Components
- **Validate cookies and headers** in Server Components

### Environment Variables

- **Use `import 'server-only'`** for code that should only be run on the server side
- **Never expose server-only env vars** to the client
- **Never pass environment variables as props to client components** unless they're suffixed with `NEXT_PUBLIC_`
- **Never use `NEXT_PUBLIC_` prefix** for sensitive data (ex. API keys, secrets)
- **Use `NEXT_PUBLIC_` prefix** only for client-safe variables

### Client Hydration Protection

- **Never expose sensitive data** in initial HTML

## Authentication & Authorization

### Row Level Security (RLS)

- **Always enable RLS** on all tables unless explicitly specified otherwise [database.mdc](mdc:.cursor/rules/database.mdc)

### Super Admin Protected Routes
Always perform extra checks when writing Super Admin code [super-admin.mdc](mdc:.cursor/rules/super-admin.mdc)

## Server Actions & API Routes

### Server Actions

- Always use `enhanceAction` wrapper for consistent security [server-actions.mdc](mdc:.cursor/rules/server-actions.mdc)
- Always use 'use server' directive at the top of the file to safely bundle server-side code
- Validate input with Zod schemas
- Implement authentication checks:

```typescript
'use server';

import { enhanceAction } from '@kit/next/actions';
import { MyActionSchema } from '../schema';

export const secureAction = enhanceAction(
  async function(data, user) {
    // Additional permission checks
    const hasPermission = await checkUserPermission(user.id, data.accountId, 'action.perform');
    if (!hasPermission) throw new Error('Insufficient permissions');

    // Validated data available
    return processAction(data);
  },
  {
    auth: true,
    schema: MyActionSchema
  }
);
```

### API Routes

- Use `enhanceRouteHandler` for consistent security [route-handlers.mdc](mdc:.cursor/rules/route-handlers.mdc)
- Implement authentication and authorization checks:

```typescript
import { enhanceRouteHandler } from '@kit/next/routes';
import { RouteSchema } from '../schema';

export const POST = enhanceRouteHandler(
  async function({ body, user, request }) {
    // Additional authorization checks
    const canAccess = await canAccessResource(user.id, body.resourceId);

    if (!canAccess) return NextResponse.json({ error: 'Forbidden' }, { status: 403 });

    // Safe to process with validated body
    return NextResponse.json({ success: true });
  },
  {
    auth: true,
    schema: RouteSchema
  }
);
```

## Client Components Security

### Context Awareness

- Use appropriate workspace contexts for access control:
    - `useUserWorkspace()` in personal account pages
    - `useTeamAccountWorkspace()` in team account pages
- Check permissions before rendering sensitive UI elements:

```typescript
function SecureComponent() {
  const { account, user } = useTeamAccountWorkspace();
  const canEdit = account.permissions.includes('entity.update');

  if (!canEdit) return null;

  return <EditComponent />;
}
```

### Data Fetching

- Use React Query with proper error handling
- Never trust client-side permission checks alone

## One-Time Tokens

Consider using OTP tokens when implementing highly destructive operations like deleting an entity that would otherwise require a full backup: [otp.mdc](mdc:.cursor/rules/otp.mdc)

## Critical Data Protection

### Personal Information

- Never log or expose sensitive user data
- Use proper session management

## Error Handling

- Never expose internal errors to clients
- Log errors securely with appropriate context
- Return generic error messages to users

```typescript
try {
  await sensitiveOperation();
} catch (error) {
  logger.error({ error, context }, 'Operation failed');
  return { error: 'Unable to complete operation' };
}
```

## Database Security

- Avoid dynamic SQL generation
- Use SECURITY DEFINER functions sparingly and carefully, warn user if you do so
- Implement proper foreign key constraints
- Use appropriate data types with constraints