255 lines
8.5 KiB
Markdown
255 lines
8.5 KiB
Markdown
# Directory Structure
|
|
|
|
This document describes the module organization and folder conventions for the frontend application.
|
|
|
|
## Overview
|
|
|
|
```
|
|
app/ # Next.js App Router
|
|
├── (marketing)/ # Public marketing pages (i18n)
|
|
│ └── [locale]/ # Locale-based routing
|
|
└── (app)/ # Protected application routes
|
|
└── app/ # Main application routes
|
|
modules/ # Feature modules
|
|
├── [feature]/ # Feature module
|
|
│ ├── components/ # UI components
|
|
│ ├── hooks/ # Custom hooks
|
|
│ ├── context/ # React Context
|
|
│ ├── lib/ # Utilities and data transforms
|
|
│ └── types/ # Frontend view model types
|
|
├── shared/ # Shared components across features
|
|
└── ui/ # UI component library
|
|
middleware.ts # Authentication & routing middleware
|
|
```
|
|
|
|
## Module Structure
|
|
|
|
### Feature Module Pattern
|
|
|
|
Each feature module should follow this structure:
|
|
|
|
```
|
|
modules/dashboard/
|
|
├── components/
|
|
│ ├── DashboardHeader.tsx
|
|
│ ├── StatsCard.tsx
|
|
│ ├── ActivityFeed.tsx
|
|
│ └── index.ts # Barrel export
|
|
├── hooks/
|
|
│ ├── useDashboardStats.ts
|
|
│ ├── useActivityFeed.ts
|
|
│ └── index.ts
|
|
├── context/
|
|
│ ├── DashboardContext.tsx
|
|
│ └── index.ts
|
|
├── lib/
|
|
│ ├── formatters.ts # Data formatting utilities
|
|
│ ├── transformers.ts # API response transformers
|
|
│ └── constants.ts # Feature-specific constants
|
|
├── types/
|
|
│ └── index.ts # View model types
|
|
└── index.ts # Public API of the module
|
|
```
|
|
|
|
### Component Organization
|
|
|
|
```
|
|
components/
|
|
├── [ComponentName].tsx # Main component file
|
|
├── [ComponentName].test.tsx # Unit tests (if applicable)
|
|
└── index.ts # Barrel export
|
|
```
|
|
|
|
### Hooks Organization
|
|
|
|
```
|
|
hooks/
|
|
├── useFeatureData.ts # Data fetching hooks
|
|
├── useFeatureActions.ts # Mutation hooks
|
|
├── useFeatureState.ts # Local state hooks
|
|
└── index.ts
|
|
```
|
|
|
|
## Shared Modules
|
|
|
|
### `modules/shared/`
|
|
|
|
Components and utilities shared across multiple features:
|
|
|
|
```
|
|
shared/
|
|
├── components/
|
|
│ ├── Layout/ # Layout components
|
|
│ ├── Navigation/ # Navigation components
|
|
│ ├── DataTable/ # Reusable data tables
|
|
│ └── Forms/ # Form components
|
|
├── hooks/
|
|
│ ├── useUser.ts # Current user hook
|
|
│ ├── useOrganization.ts # Organization context
|
|
│ └── usePermissions.ts # Permission checks
|
|
└── lib/
|
|
├── api.ts # API client configuration
|
|
└── utils.ts # Shared utilities
|
|
```
|
|
|
|
### `modules/ui/`
|
|
|
|
Low-level UI components (design system):
|
|
|
|
```
|
|
ui/
|
|
├── Button/
|
|
├── Input/
|
|
├── Select/
|
|
├── Dialog/
|
|
├── Toast/
|
|
└── ...
|
|
```
|
|
|
|
## Naming Conventions
|
|
|
|
### Files
|
|
|
|
| Type | Convention | Example |
|
|
|------|------------|---------|
|
|
| Components | PascalCase | `UserProfile.tsx` |
|
|
| Hooks | camelCase with `use` prefix | `useUserProfile.ts` |
|
|
| Context | PascalCase with `Context` suffix | `UserContext.tsx` |
|
|
| Utilities | camelCase | `formatDate.ts` |
|
|
| Constants | camelCase or SCREAMING_SNAKE_CASE | `constants.ts` |
|
|
| Types | PascalCase | `types.ts` or `UserTypes.ts` |
|
|
|
|
### Exports
|
|
|
|
Use barrel exports (`index.ts`) for clean imports:
|
|
|
|
```typescript
|
|
// modules/dashboard/components/index.ts
|
|
export { DashboardHeader } from './DashboardHeader';
|
|
export { StatsCard } from './StatsCard';
|
|
export { ActivityFeed } from './ActivityFeed';
|
|
```
|
|
|
|
```typescript
|
|
// Usage
|
|
import { DashboardHeader, StatsCard } from '@/modules/dashboard/components';
|
|
```
|
|
|
|
## Route-Module Mapping
|
|
|
|
Routes in `app/(app)/` should map to modules in `modules/`:
|
|
|
|
```
|
|
app/(app)/
|
|
├── dashboard/
|
|
│ └── page.tsx -> modules/dashboard/
|
|
├── users/
|
|
│ ├── page.tsx -> modules/users/
|
|
│ └── [id]/
|
|
│ └── page.tsx -> modules/users/ (detail view)
|
|
├── settings/
|
|
│ └── page.tsx -> modules/settings/
|
|
└── orders/
|
|
├── page.tsx -> modules/orders/
|
|
└── [id]/
|
|
└── page.tsx -> modules/orders/ (detail view)
|
|
```
|
|
|
|
## Scenario: Organization-Scoped Workspace Routes
|
|
|
|
### 1. Scope / Trigger
|
|
|
|
- Trigger: adding or changing protected app workspace routes, app-shell navigation, breadcrumbs, login redirects, organization switching, or settings pagination links.
|
|
- The current workspace URL must include the active organization slug when an organization is selected.
|
|
|
|
### 2. Signatures
|
|
|
|
- Canonical workspace route: `/app/[organizationSlug]/<workspacePath>`.
|
|
- Legacy-compatible route: `/app/<workspacePath>` may remain available while old links are migrated.
|
|
- Shared helpers live in `modules/shared/lib/workspace-routing.ts`:
|
|
- `getWorkspaceHref(organizationSlug: string | undefined, path?: string): string`
|
|
- `getWorkspacePathFromPathname(pathname: string): string`
|
|
- `getWorkspaceSlugFromPathname(pathname: string): string | undefined`
|
|
- `isWorkspacePathActive(pathname: string, itemPath: string): boolean`
|
|
|
|
### 3. Contracts
|
|
|
|
- `navGroups` stores workspace-local paths such as `/dashboard`, `/settings/users`, not full `/app/...` hrefs.
|
|
- `AppShell` passes the current `organization.slug` into navigation and logo links.
|
|
- `AppSidebarNav`, `AppBreadcrumbs`, settings search forms, and pagination must generate hrefs with `getWorkspaceHref(...)`.
|
|
- The `[organizationSlug]` layout must reject slug/session mismatches by redirecting to the active session organization workspace.
|
|
- Organization slugs cannot use reserved first-level workspace section keys such as `dashboard`, `settings`, `elders`, or `beds`.
|
|
|
|
### 4. Validation & Error Matrix
|
|
|
|
- Missing active organization slug -> helpers fall back to legacy `/app/<workspacePath>`.
|
|
- URL slug differs from `getCurrentAuthContext().organization.slug` -> redirect to `getWorkspaceHref(activeSlug, "/dashboard")`.
|
|
- New or updated organization slug equals a reserved workspace key -> API returns validation failure.
|
|
- A page-level redirect inside protected routes -> use `getWorkspaceHref(context.organization?.slug, targetPath)`.
|
|
|
|
### 5. Good/Base/Bad Cases
|
|
|
|
- Good: organization switch calls `POST /api/auth/organization`, then navigates to the same workspace path under the returned active organization slug.
|
|
- Base: legacy `/app/dashboard` remains renderable for compatibility, but new app-shell links point to `/app/{slug}/dashboard`.
|
|
- Bad: hard-code `/app/settings/users` in table forms or breadcrumbs; it drops users out of the organization-scoped workspace.
|
|
- Bad: infer tenant from `localStorage`; active organization remains server-session state.
|
|
|
|
### 6. Tests Required
|
|
|
|
- `pnpm lint`
|
|
- `pnpm type-check`
|
|
- `pnpm build`
|
|
- Browser assertions:
|
|
- login/setup lands on `/app/{activeOrg.slug}/dashboard`
|
|
- sidebar links preserve the active organization slug
|
|
- settings search and pagination preserve the active organization slug
|
|
- switching organization moves the URL to the new slug while preserving the workspace path
|
|
- mismatched `/app/{wrongSlug}/...` redirects to the active organization workspace
|
|
|
|
### 7. Wrong vs Correct
|
|
|
|
#### Wrong
|
|
|
|
```tsx
|
|
<Link href="/app/settings/users">用户管理</Link>
|
|
```
|
|
|
|
#### Correct
|
|
|
|
```tsx
|
|
<Link href={getWorkspaceHref(organization.slug, "/settings/users")}>用户管理</Link>
|
|
```
|
|
|
|
## Import Path Aliases
|
|
|
|
Configure in `tsconfig.json`:
|
|
|
|
```json
|
|
{
|
|
"compilerOptions": {
|
|
"paths": {
|
|
"@/*": ["./src/*"],
|
|
"@/modules/*": ["./modules/*"],
|
|
"@/components/*": ["./components/*"],
|
|
"@/lib/*": ["./lib/*"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Colocation**: Keep related files close together
|
|
2. **Single Responsibility**: Each module should have one clear purpose
|
|
3. **Explicit Dependencies**: Import what you need, avoid implicit globals
|
|
4. **Barrel Exports**: Use `index.ts` for public APIs
|
|
5. **Private by Default**: Only export what needs to be shared
|
|
|
|
## Anti-Patterns to Avoid
|
|
|
|
- Deeply nested folder structures (max 3-4 levels)
|
|
- Circular dependencies between modules
|
|
- Mixing feature code with shared utilities
|
|
- Importing internal module files directly (use barrel exports)
|
|
- Creating "utils" folders that become dumping grounds
|