# AI Assistant Rules
## Project Context
Crustify: Building a micro-CMS for pizzerias for French market. The goal is to be easy to have their pizzerias website with removing all the hastle.
## Tech Stack
- Bun (always use this)
- Next.js app router
- TailwindCSS
- Shadcn UI
- Supabase
- Stripe
- zsa
## App folder structure
- `[domain]` is my customers websites
- `app` is the dashboard
- `home` is the landing page for crustify website
## 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 supabase for data fetching
- Implement loading.tsx for loading states
- Use error.tsx for error handling
- for forms we use zsa to better handle server actions, refer to the zsa documentation
## 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
## ZSA Integration
### Creating your first action
```ts
// actions.ts
"use server"
import { createServerAction } from "zsa"
import z from "zod"
export const incrementNumberAction = createServerAction()
.input(z.object({
number: z.number()
}))
.handler(async ({ input }) => {
// Sleep for .5 seconds
await new Promise((resolve) => setTimeout(resolve, 500))
// Increment the input number by 1
return input.number + 1;
});
```
Let's break down the code:
createServerAction initializes a server action.
input sets the input schema for the action using a Zod schema.
handler sets the handler function for the action. The input is automatically validated based on the input schema.
A ZSAError with the code INPUT_PARSE_ERROR will be returned if the handler's input is does not match input schema.
### Calling from the server
Server actions can also be called directly from the server without the need for a try/catch block.
```ts
"use server"
const [data, err] = await incrementNumberAction({ number: 24 });
if (err) {
return;
} else {
console.log(data); // 25
}
```
However, usually you will want to use the useServerAction hook to make your life easier.
Server actions come with built-in loading states, making it easy to handle asynchronous operations. Here's an example of using the incrementNumberAction as a mutation:
```tsx
"use client"
import { incrementNumberAction } from "./actions";
import { useServerAction } from "zsa-react";
import { useState } from "react";
import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from "ui";
export default function IncrementExample() {
const [counter, setCounter] = useState(0);
const { isPending, execute, data } = useServerAction(incrementNumberAction);
return (
<Card>
<CardHeader>
<CardTitle>Increment Number</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-4">
<Button
disabled={isPending}
onClick={async () => {
const [data, err] = await execute({
number: counter,
})
if (err) {
// handle error
return
}
setCounter(data);
}}
>
Invoke action
</Button>
<p>Count:</p>
<div>{isPending ? "saving..." : data}</div>
</CardContent>
</Card>
);
}
```
### Basic Form
You can use a server action directly with a regular form. Set the type to "formData" to indicate that the input is a FormData object.
```ts
"use server"
import z from "zod"
import { createServerAction } from "zsa"
export const produceNewMessage = createServerAction()
.input(
z.object({
name: z.string().min(5),
}),
{
type: "formData",
}
)
.handler(async ({ input }) => {
await new Promise((resolve) => setTimeout(resolve, 500))
return "Hello, " + input.name
})
```
```tsx
"use client"
import { useServerAction } from "zsa-react"
import { produceNewMessage } from "./actions";
export default function FormExample() {
const {executeFormAction} = userServerAction(produceNewMessage)
return (
<form
action={executeFormAction}
>
<label>
Name:
<input type="text" name="name" required />
</label>
<button type="submit">Submit</button>
</form>
);
}
```
## General Guidance
- Ensure SEO optimization for websites 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)
bun
css
golang
javascript
next.js
react
rust
shadcn/ui
+4 more
First Time Repository
TypeScript
Languages:
CSS: 1.4KB
JavaScript: 0.3KB
TypeScript: 240.4KB
Created: 5/13/2024
Updated: 1/13/2025