# AI Assistant Rules
## Project Context
Building a marketplace for selling airsoft-related items for the French market
## Tech Stack
- PNPM (always use this)
- Next.js app router
- TailwindCSS
- Shadcn UI
- PocketBase
## Next.js Guidance
- Use Next.js app router for file-based routing
- Prefer server components over client components if possible
- If not possible, use client components with tanstack query combined with pocketbase for data fetching
- Implement loading.tsx for loading states
- Use error.tsx for error handling
- NEVER use server actions to fetch data.
## TailwindCSS Usage
- Utilize Tailwind CSS for responsive design with a mobile-first approach
- Leverage Tailwind's utility classes for rapid prototyping
## Shadcn UI Integration
- Use Shadcn UI components for consistent and accessible UI elements
- Integrate Shadcn and Tailwind for a cohesive styling approach
- The `cn` function is imported from `$/utils/cn`
## Form
- When building forms, we use ts-react-form
- Always make it via server action, by creating a co-located `actions.ts` file, where you will use zsa to create the actions.
### ts-react-form example implementation
```ts
import { createTsForm } from '@ts-react/form';
import { z } from 'zod';
// create the mapping
const mapping = [
[z.string(), TextField],
[z.boolean(), CheckBoxField],
[z.number(), NumberField],
] as const; // 👈 `as const` is necessary
// A typesafe React component
const MyForm = createTsForm(mapping);
```
```tsx
const SignUpSchema = z.object({
email: z.string().email('Enter a real email please.'), // renders TextField
password: z.string(),
address: z.string(),
favoriteColor: z.enum(['blue', 'red', 'purple']), // renders DropDownSelect and passed the enum values
isOver18: z.boolean(), // renders CheckBoxField
});
function MyPage() {
function onSubmit(data: z.infer<typeof SignUpSchema>) {
// gets typesafe data when form is submitted
}
return (
<MyForm
schema={SignUpSchema}
onSubmit={onSubmit}
renderAfter={() => <button type="submit">Submit</button>}
// optional typesafe props forwarded to your components
props={{
email: {
className: 'mt-2',
},
}}
/>
);
}
```
## PocketBase Usage
- Use Pocketbase for backend database management
- To get files or images, use `pb.files.getURL(record, filename, options)`
- Example: `pb.files.getURL(user, user.avatar, {thumb: '100x100'})`
- `pb.files.getUrl()` is deprecated, NEVER USE IT
There is 3 clients available:
- `await createStaticClient()` from `$/utils/pocketbase/static` to use when building static content that don't require auth
- `await createServerClient()` from `$/utils/pocketbase/server` to use when building server-side
- `usePocketbase()` from `$/app/pocketbase-provider` hook for client-side interaction
There is also `useUser()`, `auth()` for client and server access to the currently logged-in user.
### Filtering
The SDK comes with a helper `pb.filter(expr, params)` method to generate a filter string with placeholder parameters (`{:paramName}`) populated from an object.
The syntax basically follows the format `OPERAND OPERATOR OPERAND`, where:
- **OPERAND**: could be any field literal, string (single or double quoted), number, null, true, false
- **OPERATOR** is one of:
- `=` Equal
- `!=` NOT equal
- `>` Greater than
- `>=` Greater than or equal
- `<` Less than
- `<=` Less than or equal
- `~` Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for wildcard match)
- `!~` NOT Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for wildcard match)
- `?=` Any/At least one of Equal
- `?!=` Any/At least one of NOT equal
- `?>` Any/At least one of Greater than
- `?>=` Any/At least one of Greater than or equal
- `?<` Any/At least one of Less than
- `?<=` Any/At least one of Less than or equal
- `?~` Any/At least one of Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for wildcard match)
- `?!~` Any/At least one of NOT Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for wildcard match)
To group and combine several expressions you can use parenthesis (...), && (AND) and || (OR) tokens.
### Relations
PocketBase supports also filter, sort and expand for back-relations - relations where the associated relation field is not in the main collection.
The following notation is used: `referenceCollection_via_relField` (ex. `comments_via_post`).
For example, lets list the posts that has at least one comments record containing the word "hello":
```typescript
await pb.collection('posts').getList(1, 30, {
filter: "comments_via_post.message ?~ 'hello'",
expand: 'comments_via_post.user',
});
```
## General Guidance
- Ensure SEO optimization for marketplace visibility
- Implement internationalization to cater to the French market
- ALWAYS use the french language for the website contents
- Implement early returns for better readability
- Prefix event handlers with "handle" (handleClick, handleSubmit)
- The typescript path alias is `"$/*": ["./src/*"]`
css
dockerfile
express.js
javascript
less
next.js
npm
pnpm
+4 more
First Time Repository
TypeScript
Languages:
CSS: 2.5KB
Dockerfile: 0.4KB
JavaScript: 3.6KB
TypeScript: 483.9KB
Created: 3/21/2023
Updated: 1/20/2025