AgentSkillsCN

project-conventions

为这款 React TypeScript 管理员模板制定项目专属的编码标准、命名规范与文件夹结构。当您需要创建新文件、组件、API、钩子,或整理代码时,可选用此技能。当用户要求创建组件、页面、API、钩子、容器、路由,或任何项目文件时,应遵循这些规范。

SKILL.md
--- frontmatter
name: project-conventions
description: Project-specific coding standards, naming conventions, and folder structure for this React TypeScript admin template. Use when creating new files, components, APIs, hooks, or organizing code. Apply these conventions when the user asks to create components, pages, APIs, hooks, containers, routes, or any project files.
keywords: conventions, naming, kebab-case, folder-structure, api-structure, models, routing, i18n, no-else, utils, project-structure, file-naming, container, hooks-pattern

Project Conventions

Overview

This skill defines the coding standards and conventions for this React TypeScript admin dashboard project, including file naming, variable naming, folder structure, and architectural patterns.

File Naming Conventions

All files use kebab-case:

  • Components: user-form.tsx, data-table.tsx
  • Utilities: string.ts, object.ts, money.ts
  • Tests: {name}.test.tsx or {name}.test.ts
  • Stories: {name}.stories.tsx
  • API files: get-users.ts, create-user.ts, use-create-user.ts

Variable & Function Naming

Components

  • Pattern: PascalCase
  • Examples: Avatar, UserForm, DataTable, UserCreatePage

Hooks

  • Pattern: camelCase with use prefix
  • Examples: useUsers, useCreateUser, useIsMobile, useAuthContext

Functions

  • Pattern: camelCase
  • Examples: getUsers, createUser, formatDate, slugify

Constants

  • Pattern: UPPER_SNAKE_CASE
  • Examples: AUTHEN_TOKEN_KEY, LOCALE_KEY, MOBILE_BREAKPOINT

Types/Interfaces

  • Pattern: PascalCase
  • Examples: UserFormProps, AuthContextType, ListUsersParams

For complete naming rules, see references/rules/naming-conventions.md.

Coding Style Rules

No Else Statement (CRITICAL)

Never use else statements. Use early returns instead.

typescript
// ❌ Bad - using else
function getStatus(user: User) {
  if (user.isActive) {
    return 'active'
  } else {
    return 'inactive'
  }
}

// ✅ Good - early return
function getStatus(user: User) {
  if (user.isActive) {
    return 'active'
  }

  return 'inactive'
}

When logic is complex, extract to utility functions:

  • Feature-specific logicsrc/containers/{feature}/utils/
  • Reusable utilitiessrc/commons/

For complete patterns and examples, see references/rules/no-else.md.

Component Organization

Components follow Atomic Design:

code
src/components/
├── atoms/              # Simple components (single file)
│   ├── button.tsx
│   └── input.tsx
├── molecules/          # Complex components (folder with index.tsx)
│   ├── avatar/
│   │   ├── index.tsx
│   │   ├── avatar.stories.tsx
│   │   └── __tests__/
│   └── data-table/
│       └── index.tsx
└── box/               # Layout components
    └── page-container.tsx

Rules:

  • Simple components: Single .tsx file in atoms/
  • Complex components: Folder in molecules/ with index.tsx
  • Always add Storybook stories for molecules
  • Tests in __tests__/ subdirectory

Container/Feature Structure

Feature-based organization:

code
src/containers/{feature}/
├── index.tsx                    # Main list page
├── {feature}-create-page.tsx    # Create page
├── {feature}-edit-page.tsx      # Edit page
├── components/                  # Feature-specific components
│   └── {component}/
│       ├── index.tsx
│       └── __tests__/
└── __tests__/

For complete folder structure, see references/folder-structure.md.

API Structure

APIs separated into cores (pure functions) and hooks (React Query):

code
src/apis/{resource}/
├── cores/                 # Pure API functions
│   ├── get-{resource}s.ts
│   ├── get-{resource}-by-id.ts
│   ├── create-{resource}.ts
│   ├── update-{resource}.ts
│   └── delete-{resource}.ts
└── hooks/                 # React Query hooks
    ├── use-{resource}s.ts
    ├── use-{resource}-by-id.ts
    ├── use-create-{resource}.ts
    ├── use-update-{resource}.ts
    └── use-delete-{resource}.ts

Naming Rules:

OperationCore FileCore FunctionHook FileHook Name
Createcreate-user.tscreateUseruse-create-user.tsuseCreateUser
Updateupdate-user.tsupdateUseruse-update-user.tsuseUpdateUser
Deletedelete-user.tsdeleteUseruse-delete-user.tsuseDeleteUser
Get by IDget-user-by-id.tsgetUserByIduse-user-by-id.tsuseUserById
Get Listget-users.tsgetUsersuse-users.tsuseUsers

For complete API patterns with code examples, see references/api-patterns.md.

Model Organization

Models use decorator pattern for serialization and field mapping:

code
src/models/
├── base.ts          # Base class with common fields
├── user.ts          # User model
├── role.ts          # Role model
├── category.ts      # Category model
└── pagination.ts    # Pagination model

Model Structure:

typescript
import { field } from '@/decorators/field'
import { model } from '@/decorators/model'
import { Base } from './base'

@model()
export class User extends Base {
  @field()
  name?: string

  @field('user_id') // Map snake_case to camelCase
  userId?: string

  @field('address', Address) // Nested model
  address?: Address
}

Key Features:

  • Extend Base for common fields (id, createdAt, etc.)
  • Use @field() decorator for API-mapped properties
  • Automatic snake_case ↔ camelCase conversion
  • Support for nested models and arrays
  • fromJson() for deserialization
  • toJson() for serialization

For complete model patterns with examples, see references/model-patterns.md.

Routing Conventions

Routes follow RESTful patterns with TanStack Router:

code
src/routes/_authenticated/{resource}/
├── index.tsx        # List → /{resource}
├── create.tsx       # Create → /{resource}/create
├── $id.tsx          # Detail → /{resource}/:id
└── $id.edit.tsx     # Edit → /{resource}/:id/edit

Route Meta (REQUIRED):

typescript
export const Route = createFileRoute('/_authenticated/users/')({
  component: UserList,
  staticData: {
    meta: {
      title: 'Users', // Plain text fallback
      titleKey: 'users.title', // i18n key (REQUIRED)
    },
  },
})

For complete routing patterns, see references/routing-patterns.md.

i18n (Internationalization)

All user-facing text must use i18n translations:

typescript
import { useTranslation } from 'react-i18next'

function UserForm() {
  const { t } = useTranslation()

  return (
    <div>
      <h1>{t('users.title')}</h1>
      <button>{t('common.button.save')}</button>
    </div>
  )
}

Key Rules:

  • Never hardcode user-facing text
  • Use useTranslation() hook and t() function
  • Add keys to both src/locales/messages/en.json and vi.json
  • Organize keys hierarchically by feature
  • Memoize translated options/arrays with [t] dependency

For complete i18n patterns and examples, see references/i18n-patterns.md.

Context Structure

code
src/contexts/{feature}/
├── context.ts      # Context definition & hooks
└── provider.tsx    # Provider component

Test Organization

Tests in __tests__/ directories alongside code:

  • Component tests: {component-folder}/__tests__/{component-name}.test.tsx
  • Utility tests: {utility-folder}/__tests__/{utility-name}.test.ts
  • Hook tests: src/hooks/__tests__/{hook-name}.test.ts

Quick Checklist

When creating new code, verify:

  • No else statements - Use early returns (CRITICAL)
  • All text uses i18n - No hardcoded user-facing text (CRITICAL)
  • Translation keys added to both en.json and vi.json
  • Functions > 10 lines - Extracted to utils, or TODO(refactor) comment if not possible
  • Complex logic in feature/utils or commons/
  • File name uses kebab-case
  • Component name uses PascalCase
  • Hook name uses camelCase with use prefix
  • Function name uses camelCase
  • Constants use UPPER_SNAKE_CASE
  • Types/Interfaces use PascalCase
  • Model extends Base and uses @model() decorator
  • Model fields use @field() decorator for API mapping
  • Tests in __tests__/ directory
  • API follows cores/ and hooks/ separation
  • Route has required meta with title and titleKey
  • Complex components in molecules/ with Storybook story

Where to Place New Files

New Component?

  • Simplesrc/components/atoms/{name}.tsx
  • Complexsrc/components/molecules/{name}/index.tsx
  • Layoutsrc/components/containers/{name}.tsx

New Page?

  • Containersrc/containers/{feature}/index.tsx
  • Routesrc/routes/_authenticated/{feature}/index.tsx

New API?

  • Coresrc/apis/{resource}/cores/{action}-{resource}.ts
  • Hooksrc/apis/{resource}/hooks/use-{action}-{resource}.ts

New Model?

  • Modelsrc/models/{model}.ts

New Hook?

  • Hooksrc/hooks/{hook-name}.ts

New Context?

  • Contextsrc/contexts/{feature}/context.ts
  • Providersrc/contexts/{feature}/provider.tsx

New Utility?

  • Feature-specificsrc/containers/{feature}/utils/{category}.ts
  • Shared/Commonsrc/commons/{category}/{utility}.ts

Rule: Logic functions > 10 lines must be extracted to utils. If extraction is not possible, add // TODO(refactor): Extract to utils - ... with reason and target file.

Examples:

  • src/containers/users/utils/validation.ts - User validation logic
  • src/commons/validates/common.ts - Shared validation helpers
  • src/commons/formatters/currency.ts - Currency formatting

For when and how to extract, see references/utility-extraction.md.

Additional Resources

For detailed documentation with complete examples:

Rules (CRITICAL)

Patterns