Files
myeasycms-v2/CLAUDE.md
Giancarlo Buomprisco 1c31e7ffab 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
2025-06-08 14:31:49 +08:00

15 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

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 (port 3010)
  • /apps/e2e - Playwright end-to-end tests
  • /packages/ - Shared packages and utilities
  • /tooling/ - Build tools and development scripts

Core Technologies

  • Next.js 15 with App Router and Turbopack
  • Supabase for database, auth, and storage
  • React 19 with React Compiler
  • TypeScript with strict configuration
  • Tailwind CSS 4 for styling
  • Turborepo for monorepo management

Multi-Tenant Architecture

Uses a dual account model:

  • Personal Accounts: Individual user accounts (auth.users.id = accounts.id)
  • Team Accounts: Shared workspaces with members, roles, and permissions
  • Data associates with accounts via foreign keys for proper access control

Essential Commands

Development

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

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

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

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 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 @packages/supabase/src/types/database.ts:

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

type Account = Tables<'accounts'>;

Development Patterns

Data Fetching

  • Server Components: Use getSupabaseServerClient() from @packages/supabase/src/clients/server-client.ts
  • Client Components: Use useSupabase() hook + React Query's useQuery
  • 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:

// 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 @packages/next/src/actions/index.ts:

'use server';

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

export const createNoteAction = enhanceAction(
  async function (data, user) {
    // data is validated, user is authenticated
    return { success: true };
  },
  {
    auth: true,
    schema: CreateNoteSchema,
  },
);

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

// 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:

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:

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:

<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:

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:

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//.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:

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:

import { AdminGuard } from '@kit/admin/components/admin-guard';

export default AdminGuard(AdminPageComponent);

For admin server actions, use adminAction wrapper:

import { adminAction } from '@kit/admin';

export const yourAdminAction = adminAction(
  enhanceAction(
    async (data) => {
      // Action implementation
    },
    { schema: YourActionSchema },
  ),
);

Admin service security pattern:

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/:

// 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:

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:

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:

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:

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
  • Guard premium features with public.has_active_subscription
  • Use role hierarchy for member management
  • Primary account owners have special privileges

Permission helpers in database: apps/web/supabase/schemas/06-roles-permissions.sql

Database Development

  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

API Services

Account Services

  • 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`