Karnak19 crustify .cursorrules file for TypeScript

# 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

All Repositories (1)