Cursor rules v2 (#200)

* Add new Cursor rules based on new format
This commit is contained in:
Giancarlo Buomprisco
2025-03-03 11:38:32 +07:00
committed by GitHub
parent 784682a0f5
commit 22f78b9a86
13 changed files with 1806 additions and 463 deletions

View File

@@ -0,0 +1,77 @@
---
description: Personal Accounts context and functionality
globs: apps/*/app/home/(user),packages/features/accounts/**
alwaysApply: false
---
# Personal Account Context
This rule provides guidance for working with personal account related components in the application.
The user/personal account context in the application lives under the path `app/home/(user)`. Under this context, we identify the user using Supabase Auth.
We can use the `requireUserInServerComponent` to retrieve the relative Supabase User object and identify the user. [require-user-in-server-component.ts](mdc:apps/web/lib/server/require-user-in-server-component.ts)
### Client Components
In a Client Component, we can access the `UserWorkspaceContext` and use the `user` object to identify the user.
We can use it like this:
```tsx
import { useUserWorkspace } from '@kit/accounts/hooks/use-user-workspace';
```
This utility only works in paths under `apps/*/app/home/(user)`.
## Guidelines
### Components and Structure
- Personal account components are used in the `/home/(user)` route
- Reusable components should be in `packages/features/accounts/src/components`
- Settings-related components should be in `packages/features/accounts/src/components/personal-account-settings`
### State Management
- Use the `UserWorkspaceContext` to access user workspace data
- Personal account data can be fetched using `usePersonalAccountData` hook
- Mutations should use React Query's `useMutation` hooks
### Authentication Flow
- User authentication status is available via `useUser` hook
- Account deletion requires OTP verification
- Password updates may require reauthentication
### Feature Flags
- Personal account features are controlled via `featureFlagsConfig` [feature-flags.config.ts](mdc:apps/web/config/feature-flags.config.ts)
- Key flags:
- `enableAccountDeletion`
- `enablePasswordUpdate`
- `enablePersonalAccountBilling`
- `enableNotifications`
## Personal Account API
The API for the personal account is [api.ts](mdc:packages/features/accounts/src/server/api.ts)
A class that provides methods for interacting with account-related data in the database. Initializes a new instance of the `AccountsApi` class with a Supabase client.
### AccountsApi
```typescript
constructor(client: SupabaseClient<Database>)
```
### Methods
- `getAccount(id: string)` - Get account by ID
- `getAccountWorkspace()` - Get current user's account workspace
- `loadUserAccounts()` - Get all accounts for current user
- `getSubscription(accountId: string)` - Get account subscription
- `getOrder(accountId: string)` - Get account order
- `getCustomerId(accountId: string)` - Get account customer ID
## Database
When applying Database rules [database.mdc](mdc:.cursor/rules/database.mdc) must ensure the authenticated user matches the account ID of the entity
```sql
account_id = (select auth.uid())
```

View File

@@ -0,0 +1,91 @@
---
description: Fetch data from the Database using the Supabase Clients
globs: apps/**,packages/**
alwaysApply: false
---
# 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,
};
}
```

304
.cursor/rules/database.mdc Normal file
View File

@@ -0,0 +1,304 @@
---
description: Detailed Database Schema and Architecture
globs:
alwaysApply: 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.
## Migrations
- Migration files are placed at `apps/<app>/supabase/migrations`
- The main migration schema can be found at [20221215192558_schema.sql](mdc:apps/web/supabase/migrations/20221215192558_schema.sql)
- Use the command `pnpm --filter web supabase migrations new <name>` for creating well timestamped migrations
## 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.
## 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.
### Core Entity Relationships
1. **User ↔ Account**:
- Each user has a personal account (1:1)
- Users can belong to multiple team accounts (M:N through `accounts_memberships`)
- Accounts can have multiple users (M:N through `accounts_memberships`)
2. **Account ↔ Role**:
- Each user has a role in each account they belong to
- Roles define permissions through `role_permissions`
3. **Subscription System**:
- Accounts can have subscriptions
- Subscriptions have multiple subscription items
- Billing providers include Stripe, Lemon Squeezy, and Paddle
4. **Invitation System**:
- Team accounts can invite users via email
- Invitations specify roles for the invited user
5. **One-Time Tokens**:
- Used for secure verification processes
- Generic system that can be used for various purposes
## Table Relationships
```
auth.users
├── public.accounts (personal_account=true, id=user_id)
└── public.accounts_memberships
└── public.accounts (personal_account=false)
└── public.roles (hierarchy_level)
└── public.role_permissions
└── app_permissions (enum)
```
```
public.accounts
├── public.billing_customers
│ └── public.subscriptions
│ └── public.subscription_items
└── public.invitations
```
```
public.nonces
└── auth.users (optional relationship)
```
## Schema Overview
Makerkit implements a multi-tenant SaaS architecture through a robust account and permission system:
1. **Core Entities**:
- `auth.users`: Supabase Auth users
- `public.accounts`: Both personal and team accounts
- `public.accounts_memberships`: Links users to accounts with roles
- `public.roles` and `public.role_permissions`: Define permission hierarchy
- `public.invitations`: For inviting users to team accounts
2. **Billing System**:
- `public.billing_customers`: Account's connection to billing providers
- `public.subscriptions` and `public.subscription_items`: For subscription tracking
- `public.orders` and `public.order_items`: For one-time purchases
3. **Security**:
- `public.nonces`: One-time tokens for secure operations
## Database Best Practices
### 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`)
- **Place internal functions in the `kit` schema**
### Data Access Patterns
- Use `has_role_on_account(account_id, role?)` to check membership
- Use `has_permission(user_id, account_id, permission)` for permission checks
- Use `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;
```
- **Follow the Standard Policies Pattern**: 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
```
### 2. Account Association
- **Associate Data with Accounts**: Always link data to accounts using a foreign key:
```sql
CREATE TABLE 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 public.my_table (
status public.my_status NOT NULL DEFAULT 'pending'
);
```
- **Apply Appropriate Constraints**: Use constraints to ensure data integrity:
```sql
CREATE TABLE 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 ...
```
## 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');
```

145
.cursor/rules/forms.mdc Normal file
View File

@@ -0,0 +1,145 @@
---
description: Writing Forms with Shadcn UI, Server Actions, Zod
globs: apps/**/*.tsx,packages/**/*.tsx
alwaysApply: false
---
# 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.
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
```tsx
// _lib/server/server-actions.ts
'use server';
import { z } from 'zod';
import { enhanceAction } from '@kit/next/actions';
import { CreateNoteSchema } from '../schema/create-note.schema';
const CreateNoteSchema = z.object({
title: z.string().min(1),
content: z.string().min(1),
});
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
// _components/create-note-form.tsx
'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@kit/ui/form';
import { CreateNoteSchema } from '../_lib/schema/create-note.schema';
export function CreateNoteForm() {
const [pending, startTransition] = useTransition();
const form = useForm({
resolver: zodResolver(CreateNoteSchema),
defaultValues: {
title: '',
content: '',
},
});
const onSubmit = (data) => {
startTransition(async () => {
try {
await createNoteAction(data);
} catch {
// handle error
}
});
};
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.

View File

@@ -0,0 +1,322 @@
---
description: Creating new Pages in the app
globs: apps/**
alwaysApply: false
---
# 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
- 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

View File

@@ -0,0 +1,69 @@
---
description: Permissions
globs: apps/**,packages/**
alwaysApply: 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

View File

@@ -0,0 +1,238 @@
---
description: Detailed Project Structure of the app
globs: apps/**
alwaysApply: false
---
# 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**
- `*.loader.ts` files - Server-side data fetching functions
- 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
- `[slug]` - Dynamic route for blog posts and documentation

View File

@@ -0,0 +1,51 @@
---
description: Next.js API Endpoints/Route Handlers
globs: apps/**/route.{ts,tsx}
alwaysApply: false
---
# 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,
},
);
```

View File

@@ -0,0 +1,61 @@
---
description: Writing Server Actions for mutating data
globs: apps/**","packages/**
alwaysApply: false
---
# 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)
```tsx
'use server';
import { z } from 'zod';
import { enhanceAction } from '@kit/next/actions';
const ZodSchema = z.object({
email: z.string().email(),
password: z.string().min(6),
});
export const myServerAction = enhanceAction(
async function (data, user) {
// 1. "data" is already a valid ZodSchema and it's safe to use
// 2. "user" is the authenticated user
// ... your code here
return {
success: true,
};
},
{
auth: true,
schema: ZodSchema,
},
);
```
## Logging
Consider logging asynchronous requests using the `@kit/shared/logger` package in a structured way to provide context to the logs in both server actions and route handlers.
```tsx
const ctx = {
name: 'my-server-action', // use a meaningful name
userId: user.id, // use the authenticated user's ID
};
logger.info(ctx, 'Request started...');
const { data, error } = await supabase.from('notes').select('*');
if (error) {
logger.error(ctx, 'Request failed...');
// handle error
} else {
logger.info(ctx, 'Request succeeded...');
// use data
}
```

View File

@@ -0,0 +1,208 @@
---
description: Super Admin functionalities
globs: apps/*/app/admin/**,packages/features/admin/**
alwaysApply: false
---
1. Page Authentication:
- All pages in the admin section must be wrapped with the `AdminGuard` HOC
- This ensures only users with the 'super-admin' role and MFA enabled can access these pages
- Example: `export default AdminGuard(AdminPageComponent);`
2. Server Actions:
- Use the `adminAction` wrapper for all server actions in the admin section
- This checks if the current user is a super admin before executing the action
- Example:
```typescript
export const yourAdminAction = adminAction(
enhanceAction(
async (data) => {
// Action implementation
},
{
schema: YourActionSchema,
}
)
);
```
3. Authorization Functions:
- Import and use `isSuperAdmin` from '@kit/admin' to check if the current user is a super admin [is-super-admin.ts](mdc:packages/features/admin/src/lib/server/utils/is-super-admin.ts)
- This function returns a boolean indicating whether the user has the super-admin role and MFA enabled
- Example:
```typescript
const isAdmin = await isSuperAdmin(getSupabaseServerClient());
if (!isAdmin) {
notFound(); // or redirect/throw error
}
```
4. Schema Validation:
- Define Zod schemas for all admin actions in the 'schema' directory
- Follow the pattern in [admin-actions.schema.ts](mdc:packages/features/admin/src/lib/server/schema/admin-actions.schema.ts)
- Include appropriate validation for all fields
5. Data Fetching
- Do not use `ServerDataLoader` unless the query is very simple
- Use the authed Supabase Server Client such as [admin-dashboard.loader.ts](mdc:packages/features/admin/src/lib/server/loaders/admin-dashboard.loader.ts)
The Super Admin section requires strict access control as it provides elevated privileges. Always ensure the current user cannot perform destructive actions on their own account and properly validate input data."
## Writing Pages in the Admin Sections
1. Basic Page Structure:
```typescript
import { AdminGuard } from '@kit/admin/components/admin-guard';
import { PageBody, PageHeader } from '@kit/ui/page';
import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs';
// Optional metadata export
export const metadata = {
title: `Page Title`,
};
async function YourAdminPage() {
// Load data using cached loaders
const data = await loadYourAdminData();
return (
<>
<PageHeader description={<AppBreadcrumbs />} />
<PageBody>
{/* Page content */}
</PageBody>
</>
);
}
// IMPORTANT: Always wrap with AdminGuard
export default AdminGuard(YourAdminPage);
```
2. Data Loading:
- Create a cached loader function in a server directory
- Use the Supabase client for database operations
- Example:
```typescript
// in _lib/server/loaders/your-loader.ts
import { cache } from 'react';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
export const loadYourAdminData = cache(async () => {
const client = getSupabaseServerClient();
const { data, error } = await client.from('your_table').select('*');
if (error) throw error;
return data;
});
```
3. Dynamic Routes:
- For pages that need parameters (like `[id]`), handle them appropriately
- For example:
```typescript
interface Params {
params: Promise<{
id: string;
}>;
}
async function AdminDetailPage({ params }: Params) {
const { id } = await params;
const item = await loadItemById(id);
// ...
}
```
4. Updating Sidebar navigation at [admin-sidebar.tsx](mdc:apps/web/app/admin/_components/admin-sidebar.tsx) to include new pages
### Security Considerations:
- Validate that the target is not the current super admin
- Implement confirmation steps for destructive actions
- Never expose sensitive error details to the client
### Services
1. Basic Service Structure:
```typescript
import 'server-only';
import { SupabaseClient } from '@supabase/supabase-js';
import { Database } from '@kit/supabase/database';
import { getLogger } from '@kit/shared/logger';
export function createYourAdminService(client: SupabaseClient<Database>) {
return new YourAdminService(client);
}
class YourAdminService {
constructor(private readonly client: SupabaseClient<Database>) {}
async performAction(params: YourActionParams) {
const logger = await getLogger();
const ctx = { name: 'admin.yourService', ...params };
logger.info(ctx, 'Starting admin action');
// Perform the action
const { data, error } = await this.client
.from('your_table')
.update({ some_field: params.value })
.eq('id', params.id);
if (error) {
logger.error({ ...ctx, error }, 'Admin action failed');
throw error;
}
logger.info(ctx, 'Admin action completed successfully');
return data;
}
}
```
2. Important Patterns:
- Mark files with 'server-only' directive
- Use factory functions to create service instances
- Use class-based services with typed parameters
- Properly type the Supabase client with the Database type
- Use structured logging with context
- Handle errors consistently
3. Security Checks:
- Implement methods to verify the current user is not taking action on their own account
- Example:
```typescript
private async assertUserIsNotCurrentSuperAdmin(targetId: string) {
const { data } = await this.client.auth.getUser();
const currentUserId = data.user?.id;
if (!currentUserId) {
throw new Error(`Error fetching user`);
}
if (currentUserId === targetId) {
throw new Error(
`You cannot perform a destructive action on your own account as a Super Admin`
);
}
}
```
4. Data Access:
- Use the appropriate Supabase client (admin or regular)
- For admin-only operations, use the admin client
- For regular operations, use the standard client
- Example:
```typescript
constructor(
private readonly client: SupabaseClient<Database>,
private readonly adminClient?: SupabaseClient<Database>
) {}
```
5. Error Handling:
- Use structured error handling
- Include appropriate context in error logs
- Return typed error responses
Services should be focused on specific domains and follow the principle of single responsibility.

View File

@@ -0,0 +1,80 @@
---
description: Team Accounts context and functionality
globs: apps/*/app/home/[account],packages/features/team-accounts/**
alwaysApply: false
---
## Team Accounts
The team account context in the application lives under the path `app/home/[account]`. The `[account]` segment is the slug of the team account, from which we can identify the team.
### Accessing the Account Workspace Data in Client Components
The data fetched from the account workspace API is available in the team context. You can access this data using the `useTeamAccountWorkspace` hook [use-team-account-workspace.ts](mdc:packages/features/team-accounts/src/hooks/use-team-account-workspace.ts)
```tsx
'use client';
import { useTeamAccountWorkspace } from '@kit/team-accounts/hooks/use-team-account-workspace';
export default function SomeComponent() {
const { account, user, accounts } = useTeamAccountWorkspace();
// use account, user, and accounts
}
```
The `useTeamAccountWorkspace` hook returns the same data structure as the `loadTeamWorkspace` function.
NB: the hooks is not to be used is Server Components, only in Client Components. Additionally, this is only available in the pages under /home/[account] layout.
### Team Pages
These pages are dedicated to the team account, which means they are only accessible to team members. To access these pages, the user must be authenticated and belong to the team.
## Guidelines
### State Management
- Use the `TeamAccountWorkspaceContext` to access account workspace data
- Team account data can be accessed using `useTeamAccountWorkspace` hook
- Server-side loading done with `loadTeamWorkspace` in `team-account-workspace.loader.ts` [team-account-workspace.loader.ts](mdc:apps/web/app/home/[account]/_lib/server/team-account-workspace.loader.ts)
### Account Management Features
- Role-based permissions control what users can do within a team
- Team members can be invited, roles can be updated, and members can be removed
- Primary account owner has special privileges (transfer ownership, delete team)
- Account deletion requires OTP verification
### Billing Integration
- Team account billing uses [team-billing.service.ts](mdc:apps/web/app/home/[account]/billing/_lib/server/team-billing.service.ts)
- Per-seat billing handled by [account-per-seat-billing.service.ts](mdc:packages/features/team-accounts/src/server/services/account-per-seat-billing.service.ts)
## API
The API for the personal account is [api.ts](mdc:packages/features/team-accounts/src/server/api.ts)
### Factory
```typescript
createAccountsApi(client: SupabaseClient<Database>): AccountsApi
```
### TeamAccountsApi
```typescript
constructor(client: SupabaseClient<Database>)
```
### Methods
- `getTeamAccount(slug: string)` - Get team by slug
- `getTeamAccountById(accountId: string)` - Get team by ID
- `getSubscription(accountId: string)` - Get team subscription
- `getOrder(accountId: string)` - Get team order
- `getAccountWorkspace(slug: string)` - Get team workspace
- `hasPermission({accountId, userId, permission})` - Check user permission
- `getMembersCount(accountId: string)` - Get team member count
- `getCustomerId(accountId: string)` - Get team customer ID
- `getInvitation(adminClient, token)` - Get invitation by token
## Feature Flags
- Key flags at [feature-flags.config.ts](mdc:apps/web/config/feature-flags.config.ts)
- `enableTeamAccountBilling`
- `enableTeamDeletion`
- `enableTeamCreation`
- `enableNotifications`

160
.cursor/rules/ui.mdc Normal file
View File

@@ -0,0 +1,160 @@
---
description: UI Components API reference and guidelines
globs: **/*.tsx
alwaysApply: false
---
# 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 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` |
| `AlertDialog` | Modal dialog for important actions | `@kit/ui/alert-dialog` |
| `Alert` | Status/notification messages | `@kit/ui/alert` |
| `Avatar` | User profile images with fallback | `@kit/ui/avatar` |
| `Badge` | Small status indicators | `@kit/ui/badge` |
| `Breadcrumb` | Navigation path indicators | `@kit/ui/breadcrumb` |
| `Button` | Clickable action elements | `@kit/ui/button` |
| `Calendar` | Date picker and date display | `@kit/ui/calendar` |
| `Card` | Container for grouped content | `@kit/ui/card` |
| `Checkbox` | Selection input | `@kit/ui/checkbox` |
| `Command` | Command palette interface | `@kit/ui/command` |
| `DataTable` | Table with enhanced functionality | `@kit/ui/data-table` |
| `Dialog` | Modal window for focused interactions | `@kit/ui/dialog` |
| `DropdownMenu` | Menu triggered by a button | `@kit/ui/dropdown-menu` |
| `Form` | Form components with validation | `@kit/ui/form` |
| `Input` | Text input field | `@kit/ui/input` |
| `Label` | Text label for form elements | `@kit/ui/label` |
| `NavigationMenu` | Hierarchical navigation component | `@kit/ui/navigation-menu` |
| `Popover` | Floating content triggered by interaction | `@kit/ui/popover` |
| `RadioGroup` | Radio button selection group | `@kit/ui/radio-group` |
| `ScrollArea` | Customizable scrollable area | `@kit/ui/scroll-area` |
| `Select` | Dropdown selection menu | `@kit/ui/select` |
| `Separator` | Visual divider between content | `@kit/ui/separator` |
| `Sheet` | Sliding panel from screen edge | `@kit/ui/sheet` |
| `Sidebar` | Advanced sidebar navigation | `@kit/ui/shadcn-sidebar` |
| `Skeleton` | Loading placeholder | `@kit/ui/skeleton` |
| `Switch` | Toggle control | `@kit/ui/switch` |
| `Table` | Data display in rows and columns | `@kit/ui/table` |
| `Tabs` | Tab-based navigation | `@kit/ui/tabs` |
| `Textarea` | Multi-line text input | `@kit/ui/textarea` |
| `Tooltip` | Contextual information on hover | `@kit/ui/tooltip` |
## Makerkit-specific Components
| Component | Description | Import Path |
|-----------|-------------|-------------|
| `If` | Conditional rendering component | `@kit/ui/if` |
| `Trans` | Internationalization text component | `@kit/ui/trans` |
| `Sidebar` (Makerkit) | Navigation sidebar with context | `@kit/ui/sidebar` |
| `Page` | Page layout with navigation | `@kit/ui/page` |
| `GlobalLoader` | Full-page loading indicator | `@kit/ui/global-loader` |
| `ImageUploader` | Image upload component | `@kit/ui/image-uploader` |
| `ProfileAvatar` | User avatar with fallback | `@kit/ui/profile-avatar` |
| `DataTable` (Enhanced) | Extended data table with pagination | `@kit/ui/enhanced-data-table` |
| `Stepper` | Multi-step process indicator | `@kit/ui/stepper` |
| `CookieBanner` | GDPR-compliant cookie notice | `@kit/ui/cookie-banner` |
| `CardButton` | Card-styled button | `@kit/ui/card-button` |
| `MultiStepForm` | Form with multiple steps | `@kit/ui/multi-step-form` |
| `EmptyState` | Empty data placeholder | `@kit/ui/empty-state` |
| `AppBreadcrumbs` | Application path breadcrumbs | `@kit/ui/app-breadcrumbs` |
| `VersionUpdater` | App version update notifier | `@kit/ui/version-updater` |
## Marketing Components
Import all marketing components with:
```tsx
import {
Hero,
HeroTitle,
GradientText,
// etc.
} from '@kit/ui/marketing';
```
Key marketing components:
- `Hero`, `SecondaryHero` - Hero sections
- `FeatureCard`, `FeatureGrid` - Feature showcases
- `Footer`, `Header` - Page structure
- `NewsletterSignup` - Email collection
- `ComingSoon` - Coming soon page template
## Utility Functions
```tsx
import { cn } from '@kit/ui/utils';
```
- `cn`: Utility for conditional class name joining using Tailwind
## Common Patterns
### Conditional Rendering with If
```tsx
<If condition={isLoading} fallback={<Content />}>
<Spinner />
</If>
```
### Using Translations
```tsx
<Trans i18nKey="common:welcomeMessage" />
```
### Page Layouts
```tsx
<Page style="sidebar">
<PageNavigation>
<SidebarNavigation config={navigationConfig} />
</PageNavigation>
<PageBody>
{/* Page content */}
</PageBody>
</Page>
```
### Form with Validation
```tsx
<Form {...form}>
<FormField
name="email"
control={form.control}
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</Form>
```