diff --git a/.aiignore b/.aiignore
new file mode 100644
index 000000000..7d17f6fe3
--- /dev/null
+++ b/.aiignore
@@ -0,0 +1,27 @@
+# An .aiignore file follows the same syntax as a .gitignore file.
+# .gitignore documentation: https://git-scm.com/docs/gitignore
+
+# you can ignore files
+.DS_Store
+*.log
+*.tmp
+
+# or folders
+dist/
+build/
+out/
+
+.cursor
+.cursorignore
+database.types.ts
+playwright-report
+test-results
+web/supabase/migrations
+pnpm-lock.yaml
+.env.local
+.env.production.local
+.idea
+.vscode
+.zed
+tsconfig.tsbuildinfo
+.windsurfrules
\ No newline at end of file
diff --git a/.junie/guidelines.md b/.junie/guidelines.md
new file mode 100644
index 000000000..1074a3018
--- /dev/null
+++ b/.junie/guidelines.md
@@ -0,0 +1,1715 @@
+# 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 ;
+ }
+
+ if (userData.error) {
+ return ;
+ }
+
+ return ;
+}
+
+// Presenter component (renders UI)
+function UserProfilePresenter({ data }: { data: UserData }) {
+ return (
+
+
{data.name}
+ {/* Rest of the UI */}
+
+ );
+}
+```
+
+### 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
+
+
+// Incorrect
+
Welcome, {user.name}!
+```
+
+## 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 (
+
+
Something went wrong:
+
{error.message}
+
+
+ );
+}
+
+export function UserProfileWithErrorHandling() {
+ return (
+ {
+ // Reset application state here if needed
+ }}
+ >
+
+
+ );
+}
+```
+
+
+# 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 (
+ <>
+ }
+ description={}
+ />
+
+
+ {/* Main page content */}
+
+ >
+ );
+}
+
+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 (
+
+
+
+
+
+
+ {children}
+
+
+ );
+}
+
+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 (
+
+
+ {/* Navigation components */}
+ {children}
+
+
+ );
+}
+
+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 (
+
+
+ {/* Navigation components */}
+ {children}
+
+
+ );
+}
+
+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 (
+
+ );
+ }
+ ```
+
+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 (
+
+ {/* Component content */}
+
+ );
+ }
+ ```
+
+### 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 (
+
+ {/* Navigation items */}
+
+ );
+}
+```
+
+## 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 `` component for translated text
+ - Define translation keys in the appropriate namespace in `apps/web/public/locales//.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 (
+
+
+ );
+}
+```
+
+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 ;
+ }
+
+ return ;
+}
+```
+
+## 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/-.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
{t('homeTabLabel')}
;
+}
+```
+
+### 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 (
+
+
+
+ );
+}
+```
+
+### 4. Adding Language Selection to Your UI
+
+Use the `LanguageSelector` component:
+
+```tsx
+import { LanguageSelector } from '@kit/ui/language-selector';
+
+export function SettingsPage() {
+ return (
+
+
Language Settings
+
+
+ );
+}
+```
+
+### 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.
\ No newline at end of file
diff --git a/apps/web/app/admin/accounts/page.tsx b/apps/web/app/admin/accounts/page.tsx
index fba70ff6d..d83191bf2 100644
--- a/apps/web/app/admin/accounts/page.tsx
+++ b/apps/web/app/admin/accounts/page.tsx
@@ -1,9 +1,11 @@
import { ServerDataLoader } from '@makerkit/data-loader-supabase-nextjs';
import { AdminAccountsTable } from '@kit/admin/components/admin-accounts-table';
+import { AdminCreateUserDialog } from '@kit/admin/components/admin-create-user-dialog';
import { AdminGuard } from '@kit/admin/components/admin-guard';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs';
+import { Button } from '@kit/ui/button';
import { PageBody, PageHeader } from '@kit/ui/page';
interface SearchParams {
@@ -27,7 +29,13 @@ async function AccountsPage(props: AdminAccountsPageProps) {
return (
<>
- } />
+ }>
+