committed by
GitHub
parent
784682a0f5
commit
22f78b9a86
322
.cursor/rules/page-creation.mdc
Normal file
322
.cursor/rules/page-creation.mdc
Normal 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
|
||||
Reference in New Issue
Block a user