|
| 1 | +--- |
| 2 | +description: Rules for writing features in apps/dashboard |
| 3 | +globs: dashboard |
| 4 | +alwaysApply: false |
| 5 | +--- |
| 6 | + |
| 7 | +# Reusable Core UI Components |
| 8 | + |
| 9 | +- Always import from the central UI library under `@/components/ui/*` – e.g. `import { Button } from "@/components/ui/button"`. |
| 10 | +- Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`. |
| 11 | +- Use `NavLink` (`@/components/ui/NavLink`) for internal navigation so active states are handled automatically. |
| 12 | +- Layouts should reuse `SidebarLayout` / `FullWidthSidebarLayout` (`@/components/blocks/SidebarLayout`). |
| 13 | +- For notices & skeletons rely on `AnnouncementBanner`, `GenericLoadingPage`, `EmptyStateCard`. |
| 14 | +- Icons come from `lucide-react` or the project-specific `…/icons` exports – never embed raw SVG. |
| 15 | +- Group related components in their own folder and expose a single barrel `index.ts` where necessary. |
| 16 | +- Keep components pure; fetch data outside (server component or hook) and pass it down via props. |
| 17 | + |
| 18 | +# Styling |
| 19 | + |
| 20 | +- Tailwind CSS is **the** styling system – avoid inline styles or CSS modules. |
| 21 | +- Merge class names with `cn` from `@/lib/utils` to keep conditional logic readable. |
| 22 | +- Stick to design-tokens: background (`bg-card`), borders (`border-border`), muted text (`text-muted-foreground`) etc. |
| 23 | +- Use the `container` class with a `max-w-7xl` cap for page width consistency. |
| 24 | +- Spacing utilities (`px-*`, `py-*`, `gap-*`) are preferred over custom margins. |
| 25 | +- Responsive helpers follow mobile-first (`max-sm`, `md`, `lg`, `xl`). |
| 26 | +- Never hard-code colors – always go through Tailwind variables. |
| 27 | +- Add `className` to the root element of every component for external overrides. |
| 28 | + |
| 29 | +# Creating a new Component |
| 30 | + |
| 31 | +- Place the file close to its feature: `feature/components/MyComponent.tsx`. |
| 32 | +- Name files after the component in **PascalCase**; append `.client.tsx` when interactive. |
| 33 | +- Client components must start with `'use client';` before imports. |
| 34 | +- Accept a typed `props` object and export a **named** function (`export function MyComponent()`). |
| 35 | +- Reuse core UI primitives; avoid re-implementing buttons, cards, modals. |
| 36 | +- Combine class names via `cn`, expose `className` prop if useful. |
| 37 | +- Local state or effects live inside; data fetching happens in hooks. |
| 38 | +- Provide a Storybook story (`MyComponent.stories.tsx`) or unit test alongside the component. |
| 39 | + |
| 40 | +# When to use Server Side Rendering (Server Components) |
| 41 | + |
| 42 | +- Reading cookies/headers with `next/headers` (`getAuthToken()`, `cookies()`). |
| 43 | +- Accessing server-only environment variables or secrets. |
| 44 | +- Heavy data fetching that should not ship to the client (e.g. analytics, billing). |
| 45 | +- Redirect logic using `redirect()` from `next/navigation`. |
| 46 | +- Building layout shells (`layout.tsx`) and top-level pages that mainly assemble data. |
| 47 | +- Export default async functions without `'use client';` – they run on the Node edge. |
| 48 | +- Co-locate data helpers under `@/api/**` and mark them with `"server-only"`. |
| 49 | + |
| 50 | +# When to use Client Side Rendering (Client Components) |
| 51 | + |
| 52 | +- Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks). |
| 53 | +- Components that listen to user events, animations or live updates. |
| 54 | +- When you need access to browser APIs (localStorage, window, IntersectionObserver etc.). |
| 55 | +- Pages requiring fast transitions where data is prefetched on the client. |
| 56 | +- Anything that consumes hooks from `@tanstack/react-query` or thirdweb SDKs. |
| 57 | + |
| 58 | +# Fetching Authenticated Data – Server |
| 59 | + |
| 60 | +```ts |
| 61 | +import "server-only"; |
| 62 | +import { API_SERVER_URL } from "@/constants/env"; |
| 63 | +import { getAuthToken } from "@/app/(app)/api/lib/getAuthToken"; |
| 64 | + |
| 65 | +export async function getProjects(teamSlug: string) { |
| 66 | + const token = await getAuthToken(); |
| 67 | + if (!token) return []; |
| 68 | + const res = await fetch(`${API_SERVER_URL}/v1/teams/${teamSlug}/projects`, { |
| 69 | + headers: { Authorization: `Bearer ${token}` }, |
| 70 | + }); |
| 71 | + return res.ok ? (await res.json()).result : []; |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +Guidelines: |
| 76 | + |
| 77 | +- Always call `getAuthToken()` to get the JWT from cookies. |
| 78 | +- Prefix files with `import "server-only";` so they never end up in the client bundle. |
| 79 | +- Pass the token in the `Authorization: Bearer` header – never embed it in the URL. |
| 80 | +- Return typed results (`Project[]`, `User[]`, …) – avoid `any`. |
| 81 | + |
| 82 | +# Fetching Authenticated Data – Client |
| 83 | + |
| 84 | +```ts |
| 85 | +import { useQuery } from "@tanstack/react-query"; |
| 86 | +import { fetchJson } from "@/lib/fetch-json"; |
| 87 | + |
| 88 | +export function useProjects(teamSlug: string) { |
| 89 | + return useQuery({ |
| 90 | + queryKey: ["projects", teamSlug], |
| 91 | + queryFn: () => fetchJson(`/api/projects?team=${teamSlug}`), // internal API route handles token |
| 92 | + staleTime: 60_000, |
| 93 | + }); |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +Guidelines: |
| 98 | + |
| 99 | +- Use **React Query** (`@tanstack/react-query`) for all client data fetching. |
| 100 | +- Create light wrappers (e.g. `fetchJson`) that automatically attach the JWT from cookies/session when calling internal API routes. |
| 101 | +- Keep `queryKey` stable and descriptive for cache hits. |
| 102 | +- Prefer API routes or server actions to keep tokens secret; the browser only sees relative paths. |
| 103 | +- Configure `staleTime` / `cacheTime` according to freshness requirements. |
0 commit comments