Update Calendar component and update dependencies and updated Agents MD files (#269)
* Update Calendar component and update dependencies * Refactor AGENTS.md and CLAUDE.md for clarity and consistency - Streamlined project overview and architecture sections in both AGENTS.md and CLAUDE.md to enhance readability. - Updated command sections to reflect essential commands and improved formatting for better usability. - Clarified multi-tenant architecture details and database operations, ensuring accurate representation of the project's structure. - Enhanced component organization and security guidelines for better developer onboarding and adherence to best practices. * Refactor code for consistency and readability - Improved formatting in `env-variables-model.ts` for better description clarity. - Enhanced readability of `current-plan-alert.tsx` and `current-plan-badge.tsx` by adjusting the status badge mapping. - Standardized object syntax in `lemon-squeezy-webhook-handler.service.ts` for consistency. - Refined className ordering in `account-selector.tsx` for better maintainability. - Ensured proper syntax in `console.ts` with consistent object export. - Reorganized imports in `calendar.tsx` for clarity and structure. - Improved className formatting in `sidebar.tsx` for better readability. * Update package version to 2.10.0 in package.json
This commit is contained in:
committed by
GitHub
parent
67663b32d9
commit
1c31e7ffab
575
CLAUDE.md
575
CLAUDE.md
@@ -4,17 +4,15 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project Overview
|
||||
|
||||
Makerkit - Supabase SaaS Starter Kit (Turbo Edition) is a multi-tenant SaaS application built with Next.js, Supabase, and Tailwind CSS. The project uses a Turborepo monorepo structure with distinct apps for the main web application, development tools, and e2e testing.
|
||||
|
||||
## Architecture
|
||||
Makerkit is a multi-tenant SaaS application using a Turborepo monorepo structure with distinct apps for the main web application, development tools, and e2e testing.
|
||||
|
||||
### Monorepo Structure
|
||||
|
||||
- `/apps/web` - Main Next.js SaaS application
|
||||
- `/apps/dev-tool` - Development utilities (runs on port 3010)
|
||||
- `/apps/dev-tool` - Development utilities (port 3010)
|
||||
- `/apps/e2e` - Playwright end-to-end tests
|
||||
- `/packages/` - Shared packages and utilities
|
||||
- `/tooling/` - Build tools, linting, and development scripts
|
||||
- `/tooling/` - Build tools and development scripts
|
||||
|
||||
### Core Technologies
|
||||
|
||||
@@ -27,89 +25,101 @@ Makerkit - Supabase SaaS Starter Kit (Turbo Edition) is a multi-tenant SaaS appl
|
||||
|
||||
### Multi-Tenant Architecture
|
||||
|
||||
The application uses a dual account model:
|
||||
Uses a dual account model:
|
||||
|
||||
- **Personal Accounts**: Individual user accounts (auth.users.id = accounts.id)
|
||||
- **Personal Accounts**: Individual user accounts (`auth.users.id = accounts.id`)
|
||||
- **Team Accounts**: Shared workspaces with members, roles, and permissions
|
||||
- Data is associated with accounts via foreign keys for proper access control
|
||||
- Data associates with accounts via foreign keys for proper access control
|
||||
|
||||
## Common Commands
|
||||
## Essential Commands
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
# Start all apps in development
|
||||
pnpm dev
|
||||
|
||||
# Start specific app
|
||||
pnpm --filter web dev # Main app (port 3000)
|
||||
pnpm --filter dev-tool dev # Dev tools (port 3010)
|
||||
|
||||
# Build all apps
|
||||
pnpm build
|
||||
|
||||
# Run tests
|
||||
pnpm test # All tests
|
||||
pnpm --filter e2e test # E2E tests only
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
|
||||
```bash
|
||||
# Lint all code
|
||||
pnpm lint
|
||||
pnpm lint:fix # Auto-fix issues
|
||||
|
||||
# Format code
|
||||
pnpm format
|
||||
pnpm format:fix # Auto-fix formatting
|
||||
|
||||
# Type checking
|
||||
pnpm typecheck
|
||||
pnpm dev # Start all apps
|
||||
pnpm --filter web dev # Main app (port 3000)
|
||||
pnpm --filter dev-tool dev # Dev tools (port 3010)
|
||||
pnpm build # Build all apps
|
||||
```
|
||||
|
||||
### Database Operations
|
||||
|
||||
```bash
|
||||
# Start Supabase locally
|
||||
pnpm supabase:web:start
|
||||
|
||||
# Reset database with latest schema
|
||||
pnpm supabase:web:reset
|
||||
|
||||
# Generate TypeScript types from database
|
||||
pnpm supabase:web:typegen
|
||||
|
||||
# Run database tests
|
||||
pnpm supabase:web:test
|
||||
|
||||
# Create migration from schema changes
|
||||
pnpm --filter web supabase:db:diff
|
||||
|
||||
# Stop Supabase
|
||||
pnpm supabase:web:stop
|
||||
pnpm supabase:web:start # Start Supabase locally
|
||||
pnpm supabase:web:reset # Reset with latest schema
|
||||
pnpm supabase:web:typegen # Generate TypeScript types
|
||||
pnpm --filter web supabase:db:diff # Create migration
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
|
||||
```bash
|
||||
pnpm lint && pnpm format # Lint and format
|
||||
pnpm typecheck # Type checking
|
||||
pnpm test # Run tests
|
||||
```
|
||||
|
||||
## Application Structure
|
||||
|
||||
### Route Organization
|
||||
|
||||
```
|
||||
app/
|
||||
├── (marketing)/ # Public pages (landing, blog, docs)
|
||||
├── (auth)/ # Authentication pages
|
||||
├── home/
|
||||
│ ├── (user)/ # Personal account context
|
||||
│ └── [account]/ # Team account context ([account] = team slug)
|
||||
├── admin/ # Super admin section
|
||||
└── api/ # API routes
|
||||
```
|
||||
|
||||
See complete structure in @apps/web/app/ with examples like:
|
||||
|
||||
- Marketing layout: @apps/web/app/(marketing)/layout.tsx
|
||||
- Personal dashboard: @apps/web/app/home/(user)/page.tsx
|
||||
- Team workspace: @apps/web/app/home/[account]/page.tsx
|
||||
- Admin section: @apps/web/app/admin/page.tsx
|
||||
|
||||
### Component Organization
|
||||
|
||||
- **Route-specific**: Use `_components/` directories
|
||||
- **Route utilities**: Use `_lib/` for client, `_lib/server/` for server-side
|
||||
- **Global components**: Root-level directories
|
||||
|
||||
Example organization:
|
||||
|
||||
- Team components: @apps/web/app/home/[account]/\_components/
|
||||
- Team server utils: @apps/web/app/home/[account]/\_lib/server/
|
||||
- Marketing components: @apps/web/app/(marketing)/\_components/
|
||||
|
||||
## Database Guidelines
|
||||
|
||||
### Schema Management
|
||||
|
||||
- Database schemas are in `apps/web/supabase/schemas/`
|
||||
- Create new schemas as `<number>-<name>.sql`
|
||||
- After schema changes: run `pnpm --filter web supabase:db:diff` then `pnpm supabase:web:reset`
|
||||
|
||||
### Security & RLS
|
||||
|
||||
- **Always enable RLS** on new tables unless explicitly instructed otherwise
|
||||
- Use helper functions for access control:
|
||||
- `public.has_role_on_account(account_id, role?)` - Check team membership
|
||||
- `public.has_permission(user_id, account_id, permission)` - Check specific permissions
|
||||
- `public.is_account_owner(account_id)` - Verify account ownership
|
||||
- Associate data with accounts using foreign keys for proper access control
|
||||
- `public.has_permission(user_id, account_id, permission)` - Check permissions
|
||||
- `public.is_account_owner(account_id)` - Verify ownership
|
||||
|
||||
See RLS examples in database schemas: @apps/web/supabase/schemas/
|
||||
|
||||
### Schema Management
|
||||
|
||||
- Schemas in `apps/web/supabase/schemas/`
|
||||
- Create as `<number>-<name>.sql`
|
||||
- After changes: `pnpm --filter web supabase:db:diff` then `pnpm supabase:web:reset`
|
||||
|
||||
Key schema files:
|
||||
|
||||
- Accounts: `apps/web/supabase/schemas/03-accounts.sql`
|
||||
- Memberships: `apps/web/supabase/schemas/05-memberships.sql`
|
||||
- Permissions: `apps/web/supabase/schemas/06-roles-permissions.sql`
|
||||
|
||||
### Type Generation
|
||||
|
||||
Import auto-generated types from `@kit/supabase/database`:
|
||||
Import auto-generated types from @packages/supabase/src/types/database.ts:
|
||||
|
||||
```typescript
|
||||
import { Tables } from '@kit/supabase/database';
|
||||
@@ -121,50 +131,427 @@ type Account = Tables<'accounts'>;
|
||||
|
||||
### Data Fetching
|
||||
|
||||
- **Server Components**: Use `getSupabaseServerClient()` directly
|
||||
- **Server Components**: Use `getSupabaseServerClient()` from @packages/supabase/src/clients/server-client.ts
|
||||
- **Client Components**: Use `useSupabase()` hook + React Query's `useQuery`
|
||||
- Prefer Server Components and pass data down to Client Components when needed
|
||||
- **Admin Operations**: Use `getSupabaseServerAdminClient()` from @packages/supabase/src/clients/server-admin-client.ts (rare cases only - bypasses RLS, use with caution!)
|
||||
- Prefer Server Components and pass data down when needed
|
||||
|
||||
Use the Container/Presenter pattern for complex data components:
|
||||
|
||||
```typescript
|
||||
// Container: handles data fetching
|
||||
function UserProfileContainer() {
|
||||
const userData = useUserData();
|
||||
return <UserProfilePresenter data={userData.data} />;
|
||||
}
|
||||
|
||||
// Presenter: handles UI rendering
|
||||
function UserProfilePresenter({ data }: { data: UserData }) {
|
||||
return <div>{data.name}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
Example server-side data loading:
|
||||
|
||||
- User workspace loader: @apps/web/app/home/(user)/\_lib/server/load-user-workspace.ts
|
||||
- Team workspace loader: @apps/web/app/home/[account]/\_lib/server/team-account-workspace.loader.ts
|
||||
- Data provider pattern: @packages/features/team-accounts/src/components/members/roles-data-provider.tsx
|
||||
|
||||
### Server Actions
|
||||
|
||||
- Always use `enhanceAction` from `@kit/next/actions`
|
||||
- Name files as `server-actions.ts` and functions with `Action` suffix
|
||||
- Place Zod schemas in separate files for reuse with forms
|
||||
- Use `'use server'` directive at top of file
|
||||
Always use `enhanceAction` from @packages/next/src/actions/index.ts:
|
||||
|
||||
### Error Handling & Logging
|
||||
```typescript
|
||||
'use server';
|
||||
|
||||
- Use `@kit/shared/logger` for logging
|
||||
- Don't swallow errors - handle them appropriately
|
||||
- Provide context without sensitive data
|
||||
import { enhanceAction } from '@kit/next/actions';
|
||||
|
||||
### Component Organization
|
||||
export const createNoteAction = enhanceAction(
|
||||
async function (data, user) {
|
||||
// data is validated, user is authenticated
|
||||
return { success: true };
|
||||
},
|
||||
{
|
||||
auth: true,
|
||||
schema: CreateNoteSchema,
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
- Route-specific components in `_components/` directories
|
||||
- Route-specific utilities in `_lib/` directories
|
||||
- Server-side utilities in `_lib/server/`
|
||||
- Global components and utilities in root-level directories
|
||||
Example server actions:
|
||||
|
||||
- Team billing: @apps/web/app/home/[account]/billing/\_lib/server/server-actions.ts
|
||||
- Personal settings: @apps/web/app/home/(user)/settings/\_lib/server/server-actions.ts
|
||||
|
||||
### Forms with React Hook Form & Zod
|
||||
|
||||
```typescript
|
||||
// 1. Define schema in separate file
|
||||
export const CreateNoteSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
});
|
||||
|
||||
// 2. Client component with form
|
||||
('use client');
|
||||
const form = useForm({
|
||||
resolver: zodResolver(CreateNoteSchema),
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
startTransition(async () => {
|
||||
await toast.promise(createNoteAction(data), {
|
||||
loading: 'Creating...',
|
||||
success: 'Created!',
|
||||
error: 'Failed!',
|
||||
});
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
See form examples:
|
||||
|
||||
- Contact form: @apps/web/app/(marketing)/contact/\_components/contact-form.tsx
|
||||
- Verify OTP form: @packages/otp/src/components/verify-otp-form.tsx
|
||||
|
||||
### Route Handlers (API Routes)
|
||||
|
||||
Use `enhanceRouteHandler` from @packages/next/src/routes/index.ts:
|
||||
|
||||
```typescript
|
||||
import { enhanceRouteHandler } from '@kit/next/routes';
|
||||
|
||||
export const POST = enhanceRouteHandler(
|
||||
async function ({ body, user, request }) {
|
||||
// body is validated, user available if auth: true
|
||||
return NextResponse.json({ success: true });
|
||||
},
|
||||
{
|
||||
auth: true,
|
||||
schema: ZodSchema,
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
Example API routes:
|
||||
|
||||
- Billing webhook: @apps/web/app/api/billing/webhook/route.ts
|
||||
- Database webhook: @apps/web/app/api/db/webhook/route.ts
|
||||
|
||||
## React & TypeScript Best Practices
|
||||
|
||||
### Components
|
||||
|
||||
- Use functional components with TypeScript
|
||||
- Always use `'use client'` directive for client components
|
||||
- Destructure props with proper TypeScript interfaces
|
||||
|
||||
### Conditional Rendering
|
||||
|
||||
Use the `If` component from @packages/ui/src/makerkit/if.tsx:
|
||||
|
||||
```tsx
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Spinner } '@kit/ui/spinner';
|
||||
|
||||
<If condition={isLoading} fallback={<Content />}>
|
||||
<Spinner />
|
||||
</If>
|
||||
|
||||
// With type inference
|
||||
<If condition={error}>
|
||||
{(err) => <ErrorMessage error={err} />}
|
||||
</If>
|
||||
```
|
||||
|
||||
### Testing Attributes
|
||||
|
||||
Add data attributes for testing:
|
||||
|
||||
```tsx
|
||||
<button data-test="submit-button">Submit</button>
|
||||
<div data-test="user-profile" data-user-id={user.id}>Profile</div>
|
||||
<form data-test="signup-form">Form content</form>
|
||||
```
|
||||
|
||||
### Internationalization
|
||||
|
||||
Always use `Trans` component from @packages/ui/src/makerkit/trans.tsx:
|
||||
|
||||
```tsx
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
// Basic usage
|
||||
<Trans
|
||||
i18nKey="user:welcomeMessage"
|
||||
values={{ name: user.name }}
|
||||
defaults="Welcome, {name}!"
|
||||
/>
|
||||
|
||||
// With HTML elements
|
||||
<Trans
|
||||
i18nKey="terms:agreement"
|
||||
components={{
|
||||
TermsLink: <a href="/terms" className="underline" />,
|
||||
}}
|
||||
defaults="I agree to the <TermsLink>Terms</TermsLink>."
|
||||
/>
|
||||
|
||||
// Pluralization
|
||||
<Trans
|
||||
i18nKey="notifications:count"
|
||||
count={notifications.length}
|
||||
defaults="{count, plural, =0 {No notifications} one {# notification} other {# notifications}}"
|
||||
/>
|
||||
```
|
||||
|
||||
Use `LanguageSelector` component from @packages/ui/src/makerkit/language-selector.tsx:
|
||||
|
||||
```tsx
|
||||
import { LanguageSelector } from '@kit/ui/language-selector';
|
||||
|
||||
<LanguageSelector />;
|
||||
```
|
||||
|
||||
Adding new languages:
|
||||
|
||||
1. Add language code to @apps/web/lib/i18n/i18n.settings.ts
|
||||
2. Create translation files in @apps/web/public/locales/[new-language]/
|
||||
3. Copy structure from English files as template
|
||||
|
||||
Adding new namespaces:
|
||||
|
||||
1. Add namespace to `defaultI18nNamespaces` in @apps/web/lib/i18n/i18n.settings.ts
|
||||
2. Create corresponding translation files for all supported languages
|
||||
|
||||
Translation files located in @apps/web/public/locales/<locale>/<namespace>.json:
|
||||
|
||||
- Common translations: @apps/web/public/locales/en/common.json
|
||||
- Auth translations: @apps/web/public/locales/en/auth.json
|
||||
- Team translations: @apps/web/public/locales/en/teams.json
|
||||
|
||||
## Security Guidelines
|
||||
|
||||
### Authentication & Authorization
|
||||
|
||||
- Authentication enforced by middleware
|
||||
- Authorization typically handled by RLS at database level, unless using the admin client
|
||||
- For rare admin client usage, enforce both manually
|
||||
- User authentication helper: @apps/web/lib/server/require-user-in-server-component.ts if required or to obtain the authed user
|
||||
|
||||
### Data Passing
|
||||
|
||||
- **Never pass sensitive data** to Client Components
|
||||
- **Never expose server environment variables** to client (unless `NEXT_PUBLIC_`)
|
||||
- Always validate user input before processing
|
||||
|
||||
### OTP for Sensitive Operations
|
||||
|
||||
Use one-time tokens from @packages/otp/src/api/index.ts for destructive operations:
|
||||
|
||||
```tsx
|
||||
import { VerifyOtpForm } from '@kit/otp/components';
|
||||
|
||||
<VerifyOtpForm
|
||||
purpose="account-deletion"
|
||||
email={user.email}
|
||||
onSuccess={(otp) => {
|
||||
// Proceed with verified operation
|
||||
}}
|
||||
/>;
|
||||
```
|
||||
|
||||
OTP schema and functions: @apps/web/supabase/schemas/12-one-time-tokens.sql
|
||||
|
||||
### Super Admin Protection
|
||||
|
||||
For admin routes, use `AdminGuard` from @packages/features/admin/src/components/admin-guard.tsx:
|
||||
|
||||
```tsx
|
||||
import { AdminGuard } from '@kit/admin/components/admin-guard';
|
||||
|
||||
export default AdminGuard(AdminPageComponent);
|
||||
```
|
||||
|
||||
For admin server actions, use `adminAction` wrapper:
|
||||
|
||||
```tsx
|
||||
import { adminAction } from '@kit/admin';
|
||||
|
||||
export const yourAdminAction = adminAction(
|
||||
enhanceAction(
|
||||
async (data) => {
|
||||
// Action implementation
|
||||
},
|
||||
{ schema: YourActionSchema },
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
Admin service security pattern:
|
||||
|
||||
```typescript
|
||||
private async assertUserIsNotCurrentSuperAdmin(targetId: string) {
|
||||
const { data } = await this.client.auth.getUser();
|
||||
const currentUserId = data.user?.id;
|
||||
|
||||
if (currentUserId === targetId) {
|
||||
throw new Error('Cannot perform destructive action on your own account');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## UI Components
|
||||
|
||||
### Core UI Library
|
||||
|
||||
Import from @packages/ui/src/:
|
||||
|
||||
```tsx
|
||||
// Shadcn components
|
||||
import { Button } from '@kit/ui/button';
|
||||
// @packages/ui/src/shadcn/button.tsx
|
||||
import { Card } from '@kit/ui/card';
|
||||
// @packages/ui/src/shadcn/sonner.tsx
|
||||
|
||||
// Makerkit components
|
||||
import { If } from '@kit/ui/if';
|
||||
// @packages/ui/src/makerkit/trans.tsx
|
||||
import { ProfileAvatar } from '@kit/ui/profile-avatar';
|
||||
// @packages/ui/src/shadcn/card.tsx
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
// @packages/ui/src/makerkit/if.tsx
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
// @packages/ui/src/makerkit/profile-avatar.tsx
|
||||
```
|
||||
|
||||
### Key Component Categories
|
||||
|
||||
- **Forms**: Form components in @packages/ui/src/shadcn/form.tsx
|
||||
- **Navigation**: Navigation menu in @packages/ui/src/shadcn/navigation-menu.tsx
|
||||
- **Data Display**: Data table in @packages/ui/src/makerkit/data-table.tsx
|
||||
- **Marketing**: Marketing components in @packages/ui/src/makerkit/marketing/
|
||||
|
||||
### Styling
|
||||
|
||||
- Use Tailwind CSS with semantic classes
|
||||
- Prefer `bg-background`, `text-muted-foreground` over fixed colors
|
||||
- Use `cn()` utility from @packages/ui/src/lib/utils.ts for class merging
|
||||
|
||||
## Workspace Contexts
|
||||
|
||||
### Personal Account Context (`/home/(user)`)
|
||||
|
||||
Use hook from `packages/features/accounts/src/hooks/use-user-workspace.ts`:
|
||||
|
||||
```tsx
|
||||
import { useUserWorkspace } from '@kit/accounts/hooks/use-user-workspace';
|
||||
|
||||
function PersonalComponent() {
|
||||
const { user, account } = useUserWorkspace();
|
||||
// Personal account data
|
||||
}
|
||||
```
|
||||
|
||||
Context provider: `packages/features/accounts/src/components/user-workspace-context-provider.tsx`
|
||||
|
||||
### Team Account Context (`/home/[account]`)
|
||||
|
||||
Use hook from `packages/features/team-accounts/src/hooks/use-team-account-workspace.ts`:
|
||||
|
||||
```tsx
|
||||
import { useTeamAccountWorkspace } from '@kit/team-accounts/hooks/use-team-account-workspace';
|
||||
|
||||
function TeamComponent() {
|
||||
const { account, user, accounts } = useTeamAccountWorkspace();
|
||||
// Team account data with permissions
|
||||
}
|
||||
```
|
||||
|
||||
Context provider: `packages/features/team-accounts/src/components/team-account-workspace-context-provider.tsx`
|
||||
|
||||
## Error Handling & Logging
|
||||
|
||||
### Structured Logging
|
||||
|
||||
Use logger from `packages/shared/src/logger/logger.ts`:
|
||||
|
||||
```typescript
|
||||
import { getLogger } from '@kit/shared/logger';
|
||||
|
||||
const logger = await getLogger();
|
||||
const ctx = { name: 'myOperation', userId: user.id };
|
||||
|
||||
logger.info(ctx, 'Operation started');
|
||||
// ... operation
|
||||
logger.error({ ...ctx, error }, 'Operation failed');
|
||||
```
|
||||
|
||||
### Error Boundaries
|
||||
|
||||
Use proper error handling with meaningful user messages:
|
||||
|
||||
```tsx
|
||||
try {
|
||||
await operation();
|
||||
} catch (error) {
|
||||
logger.error({ error, context }, 'Operation failed');
|
||||
return { error: 'Unable to complete operation' }; // Generic message
|
||||
}
|
||||
```
|
||||
|
||||
## Feature Development Workflow
|
||||
|
||||
### Creating New Pages
|
||||
|
||||
1. Create page component in appropriate route group
|
||||
2. Add `withI18n()` HOC from `apps/web/lib/i18n/with-i18n.tsx`
|
||||
3. Implement `generateMetadata()` for SEO
|
||||
4. Add loading state with `loading.tsx`
|
||||
5. Create components in `_components/` directory
|
||||
6. Add server utilities in `_lib/server/`
|
||||
|
||||
Example page structure:
|
||||
|
||||
- Marketing page: `apps/web/app/(marketing)/pricing/page.tsx`
|
||||
- Dashboard page: `apps/web/app/home/(user)/page.tsx`
|
||||
- Team page: `apps/web/app/home/[account]/members/page.tsx`
|
||||
|
||||
### Permission Patterns
|
||||
|
||||
- Check permissions before data operations using helper functions
|
||||
- Guard premium features with subscription checks (`public.has_active_subscription`)
|
||||
- Use role hierarchy to control member management actions
|
||||
- Primary account owners have special privileges that cannot be revoked
|
||||
- Check permissions before data operations
|
||||
- Guard premium features with `public.has_active_subscription`
|
||||
- Use role hierarchy for member management
|
||||
- Primary account owners have special privileges
|
||||
|
||||
## Testing
|
||||
Permission helpers in database: `apps/web/supabase/schemas/06-roles-permissions.sql`
|
||||
|
||||
### E2E Tests
|
||||
### Database Development
|
||||
|
||||
```bash
|
||||
pnpm --filter e2e test # Run all E2E tests
|
||||
```
|
||||
1. Create schema file: `apps/web/supabase/schemas/<number>-<name>.sql`
|
||||
2. Enable RLS and create policies
|
||||
3. Generate migration: `pnpm --filter web supabase:db:diff`
|
||||
4. Reset database: `pnpm supabase:web:reset`
|
||||
5. Generate types: `pnpm supabase:web:typegen`
|
||||
|
||||
Test files are in `apps/e2e/tests/` organized by feature area.
|
||||
## API Services
|
||||
|
||||
## Important Notes
|
||||
### Account Services
|
||||
|
||||
- Uses pnpm as package manager
|
||||
- Database types are auto-generated - don't write manually if shape matches DB
|
||||
- Always use explicit schema references in SQL (`public.table_name`)
|
||||
- Documentation available at: https://makerkit.dev/docs/next-supabase-turbo/introduction
|
||||
- Personal accounts API: `packages/features/accounts/src/server/api.ts`
|
||||
- Team accounts API: `packages/features/team-accounts/src/server/api.ts`
|
||||
- Admin service: `packages/features/admin/src/lib/server/services/admin.service.ts`
|
||||
|
||||
### Billing Services
|
||||
|
||||
- Personal billing: `apps/web/app/home/(user)/billing/_lib/server/user-billing.service.ts`
|
||||
- Team billing: `apps/web/app/home/[account]/billing/_lib/server/team-billing.service.ts`
|
||||
- Per-seat billing: `packages/features/team-accounts/src/server/services/account-per-seat-billing.service.ts`
|
||||
|
||||
## Key Configuration Files
|
||||
|
||||
- **Feature flags**: @apps/web/config/feature-flags.config.ts
|
||||
- **i18n settings**: @apps/web/lib/i18n/i18n.settings.ts
|
||||
- **Supabase local config**: @apps/web/supabase/config.toml@
|
||||
- **Middleware**: @apps/web/middleware.ts`
|
||||
Reference in New Issue
Block a user