mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-18 10:01:56 +00:00
1324 lines
44 KiB
Markdown
1324 lines
44 KiB
Markdown
# Prowler UI Agent Guide
|
|
|
|
**Complete guide for AI agents and developers working on the Prowler UI Next.js application.**
|
|
|
|
## Mission & Scope
|
|
|
|
- Ship small, high-impact UI changes with minimal risk
|
|
- Align to current patterns: App Router, Server Components first, consistent styling, strict types
|
|
- Avoid broad refactors, library swaps, or reorganization unless requested
|
|
- Focus on safe, incremental frontend changes aligned with existing architecture
|
|
|
|
---
|
|
|
|
## Critical Architecture Rules (Non-Negotiable)
|
|
|
|
### 1. React Imports (Required)
|
|
|
|
**NEVER** import React with `import * as React`. Instead, import only what you need:
|
|
|
|
**❌ DON'T:**
|
|
|
|
```typescript
|
|
import * as React from "react";
|
|
import React, { useState } from "react";
|
|
```
|
|
|
|
**✅ DO:**
|
|
|
|
```typescript
|
|
import { useState, useEffect } from "react";
|
|
```
|
|
|
|
**When you need nothing from React (e.g., only using JSX):**
|
|
|
|
```typescript
|
|
// No React import needed - JSX works without it in modern Next.js
|
|
export function MyComponent() {
|
|
return <div>Hello</div>;
|
|
}
|
|
```
|
|
|
|
### 2. TypeScript Type Patterns (Required)
|
|
|
|
When defining union types for options, ALWAYS create a const object first, then extract the type:
|
|
|
|
**❌ DON'T:**
|
|
|
|
```typescript
|
|
type SortOption = "high-low" | "low-high" | "alphabetical";
|
|
```
|
|
|
|
**✅ DO:**
|
|
|
|
```typescript
|
|
const SORT_OPTIONS = {
|
|
HIGH_LOW: "high-low",
|
|
LOW_HIGH: "low-high",
|
|
ALPHABETICAL: "alphabetical",
|
|
} as const;
|
|
|
|
type SortOption = (typeof SORT_OPTIONS)[keyof typeof SORT_OPTIONS];
|
|
```
|
|
|
|
### 2. Tailwind 4 Theme Variables
|
|
|
|
This project uses Tailwind 4 with @theme variables. **Tailwind is mainly semantic** - prioritize using Tailwind's naming system whenever possible.
|
|
|
|
#### In Template/JSX (className)
|
|
|
|
- ✅ Use Tailwind utility classes: `bg-card-bg`, `text-white`, `text-slate-400`, `border-slate-700`
|
|
- ✅ Use arbitrary values with classes: `h-3`, `w-3`, `min-w-[200px]`, `bg-slate-700/50`
|
|
- ✅ Use Tailwind for conditional styles: `className={isActive ? "bg-blue-500" : "bg-gray-500"}`
|
|
- ✅ Use style props only for truly dynamic values: `style={{ width: \`\${percentage}%\` }}`
|
|
- ❌ NEVER use `var()` in className
|
|
- ❌ NEVER use hex colors in className
|
|
|
|
#### Constants with var() (Only for library props that don't accept className)
|
|
|
|
- ✅ Use CHART_COLORS constants: `stroke={CHART_COLORS.gridLine}`, `tick={{ fill: CHART_COLORS.textSecondary }}`
|
|
- These props don't accept className, so we use constants that internally reference `var()`
|
|
- This is the **only** valid use case for `var()` - when the library doesn't support className
|
|
|
|
#### Examples
|
|
|
|
```tsx
|
|
// ✅ GOOD - Template with Tailwind classes
|
|
<div className="rounded-lg border border-slate-700 bg-slate-800 p-3">
|
|
<p className="text-sm font-semibold text-white">{title}</p>
|
|
<Bell size={14} className="text-slate-400" />
|
|
</div>
|
|
|
|
// ✅ GOOD - Conditional Tailwind classes
|
|
<button className={isActive ? "bg-blue-500" : "bg-gray-500"}>
|
|
Click me
|
|
</button>
|
|
|
|
// ✅ GOOD - Recharts library props with CHART_COLORS (var() only here)
|
|
<XAxis tick={{ fill: CHART_COLORS.textSecondary, fontSize: 12 }} />
|
|
<CartesianGrid stroke={CHART_COLORS.gridLine} />
|
|
|
|
// ✅ GOOD - Truly dynamic values (not available in Tailwind)
|
|
<div style={{ width: `${percentage}%`, opacity: isFaded ? 0.5 : 1 }} />
|
|
|
|
// ❌ BAD - var() in className
|
|
<div className="bg-[var(--color-card-bg)]" /> // Don't do this!
|
|
|
|
// ❌ BAD - Hex colors in className
|
|
<p className="text-[#ffffff]" /> // Use text-white instead
|
|
|
|
// ❌ BAD - Using var() for colors when Tailwind classes exist
|
|
const PROVIDER_COLORS = {
|
|
AWS: "var(--color-orange)", // Use Tailwind classes instead!
|
|
};
|
|
|
|
// ❌ BAD - Using style when className is available
|
|
<div style={{ backgroundColor: "blue" }} /> // Use className="bg-blue-500" instead
|
|
```
|
|
|
|
### 3. The `cn()` Utility Function
|
|
|
|
#### What is `cn()`?
|
|
|
|
The `cn()` function is a utility that combines `clsx` and `tailwind-merge`:
|
|
|
|
```typescript
|
|
import { clsx } from "clsx";
|
|
import { twMerge } from "tailwind-merge";
|
|
|
|
export function cn(...inputs: ClassValue[]) {
|
|
return twMerge(clsx(inputs));
|
|
}
|
|
```
|
|
|
|
**Components:**
|
|
|
|
- **`clsx`**: Constructs conditional className strings (handles booleans, arrays, objects)
|
|
- **`twMerge`**: Intelligently merges Tailwind classes, resolving conflicts (e.g., `p-4` + `p-2` → `p-2`)
|
|
|
|
#### When to Use `cn()`
|
|
|
|
Use `cn()` **ONLY** when you have:
|
|
|
|
##### 1. Conditional Classes
|
|
|
|
```tsx
|
|
// ✅ GOOD - Conditional logic
|
|
<div className={cn("h-3 w-3", isCircle ? "rounded-full" : "rounded-sm")} />
|
|
|
|
// ✅ GOOD - Boolean conditionals
|
|
<button className={cn("btn", isActive && "btn-active", isDisabled && "opacity-50")} />
|
|
```
|
|
|
|
##### 2. Merging Props with Conflicting Classes
|
|
|
|
```tsx
|
|
// ✅ GOOD - Component accepting className prop
|
|
interface ButtonProps {
|
|
className?: string;
|
|
}
|
|
|
|
function Button({ className }: ButtonProps) {
|
|
return <button className={cn("bg-blue-500 px-4 py-2", className)} />;
|
|
}
|
|
|
|
// Usage: <Button className="px-6" /> → Results in "px-6 py-2 bg-blue-500"
|
|
```
|
|
|
|
##### 3. Dynamic String Interpolation
|
|
|
|
```tsx
|
|
// ✅ GOOD - Dynamic values that need proper merging
|
|
<span className={cn(`text-${size}`, "font-semibold text-white")} />
|
|
```
|
|
|
|
#### When NOT to Use `cn()`
|
|
|
|
**DO NOT** use `cn()` for static classes without conditional logic:
|
|
|
|
```tsx
|
|
// ❌ BAD - Unnecessary, no conditional logic
|
|
<div className={cn("rounded-lg border border-slate-700 bg-slate-800 p-3")} />
|
|
|
|
// ✅ GOOD - Just use className directly
|
|
<div className="rounded-lg border border-slate-700 bg-slate-800 p-3" />
|
|
|
|
// ❌ BAD - No conflicts or conditionals
|
|
<div className={cn("flex items-center gap-2")} />
|
|
|
|
// ✅ GOOD - Static classes don't need cn()
|
|
<div className="flex items-center gap-2" />
|
|
```
|
|
|
|
#### Real-World Examples
|
|
|
|
```tsx
|
|
// ❌ BAD - Overuse of cn()
|
|
function Tooltip({ active, payload }: any) {
|
|
return (
|
|
<div className={cn("rounded-lg border border-slate-700")}>
|
|
<div className={cn("flex items-center gap-2")}>
|
|
<div className={cn("h-3 w-3 rounded-sm")} />
|
|
<span className={cn("text-sm font-semibold text-white")}>
|
|
{payload.name}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ✅ GOOD - Only use cn() where needed
|
|
function Tooltip({ active, payload, shape }: any) {
|
|
return (
|
|
<div className="rounded-lg border border-slate-700 bg-slate-800 p-3">
|
|
<div className="flex items-center gap-2">
|
|
<div
|
|
className={cn(
|
|
"h-3 w-3",
|
|
shape === "circle" ? "rounded-full" : "rounded-sm",
|
|
)}
|
|
/>
|
|
<span className="text-sm font-semibold text-white">{payload.name}</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Key Takeaway
|
|
|
|
**`cn()` is a tool for conditional logic and conflict resolution, NOT a wrapper for every className.**
|
|
|
|
Use it purposefully where it adds value. Don't use it out of habit.
|
|
|
|
**Reference:** [The Story Behind Tailwind's cn() Function](https://tigerabrodi.blog/the-story-behind-tailwinds-cn-function)
|
|
|
|
### 4. React 19 with Compiler
|
|
|
|
This project uses React 19 with the React Compiler enabled. This means:
|
|
|
|
- **DO NOT use `useMemo`** - React Compiler handles memoization automatically
|
|
- **DO NOT use `useCallback`** - React Compiler optimizes callbacks automatically
|
|
- Only use these hooks if you have a specific, documented reason that the compiler cannot handle
|
|
|
|
### 5. Next.js 15 Architecture Principles
|
|
|
|
#### 1. App Router Architecture First
|
|
|
|
- **ALL routes MUST use App Router** - never use Pages Router for new projects
|
|
- Leverage Server Components by default, Client Components only when necessary
|
|
- Use proper file conventions: `page.tsx`, `layout.tsx`, `loading.tsx`, `error.tsx`, `not-found.tsx`
|
|
- Implement route groups `(group-name)` for organization without affecting URL structure
|
|
- Use private folders `_folder` to opt out of routing system
|
|
|
|
#### 2. Server-First Architecture
|
|
|
|
- **Server Components by default** - add `"use client"` only when required
|
|
- Optimize data fetching at the server level
|
|
- Implement streaming with `loading.tsx` and Suspense boundaries
|
|
- Use Server Actions for form handling and mutations
|
|
- Leverage static generation and ISR for performance
|
|
- Use DAL (Data Access Layer) patterns to separate data logic
|
|
- To prevent accidental usage in Client Components, you can use the server-only package, this is a MUST for Server Actions and recommended for all server-only code
|
|
|
|
#### 3. The Scope Rule - Your Unbreakable Law
|
|
|
|
**"Scope determines structure"**
|
|
|
|
- Code used by 2+ features → MUST go in global/shared directories
|
|
- Code used by 1 feature → MUST stay local in that feature
|
|
- NO EXCEPTIONS - This rule is absolute and non-negotiable
|
|
|
|
#### 4. Screaming Architecture
|
|
|
|
Your structures must IMMEDIATELY communicate what the application does:
|
|
|
|
- Feature names must describe business functionality, not technical implementation
|
|
- Directory structure should tell the story of what the app does at first glance
|
|
- Route structure should mirror business logic, not technical concerns
|
|
|
|
#### 5. Component Placement Decision Framework
|
|
|
|
When analyzing where to place a component, you MUST follow this process:
|
|
|
|
1. **Identify component type**: Server Component, Client Component, or hybrid
|
|
2. **Count usage**: Identify exactly how many features/routes use the component
|
|
3. **Apply the Scope Rule**:
|
|
- Used by 1 feature → Local placement within that feature
|
|
- Used by 2+ features → Global/shared directory
|
|
4. **Consider performance**: Optimize bundle splitting and server-side rendering
|
|
5. **Document the decision**: Always explain WHY the placement was chosen
|
|
|
|
### 6. File Organization Patterns
|
|
|
|
#### Co-location Principles
|
|
|
|
Follow these patterns for organizing related files:
|
|
|
|
**For Server Actions:**
|
|
|
|
```
|
|
ui/actions/
|
|
└── feature-name/
|
|
├── feature-name.ts # Server actions
|
|
├── models.ts # Domain models/types
|
|
└── feature-name.adapter.ts # Data transformations (if needed)
|
|
```
|
|
|
|
**For Components:**
|
|
|
|
```
|
|
ui/components/
|
|
└── feature-name/
|
|
├── feature-component.tsx # Main component
|
|
├── feature-client.tsx # Client-specific logic
|
|
└── feature-name/ # Utilities folder
|
|
├── types.ts
|
|
├── utils.ts
|
|
├── constants.ts
|
|
└── hooks.ts
|
|
```
|
|
|
|
**Key Rules:**
|
|
|
|
- Models used by only one feature → Keep local in that feature's directory
|
|
- Utilities used by only one feature → Keep in feature's utils folder
|
|
- Types only used within a feature → Keep in feature's types file
|
|
- Only move to global/shared when used by 2+ features
|
|
|
|
---
|
|
|
|
## Project Overview
|
|
|
|
The Prowler UI is a Next.js 15 application providing a modern web interface for the Prowler security platform. It features a comprehensive dashboard for managing cloud security scans, compliance frameworks, and findings across multiple cloud providers.
|
|
|
|
## Tech Stack (Updated January 2025)
|
|
|
|
- **Framework**: Next.js 15.5.3 with App Router
|
|
- **Runtime**: React 19.1.1 (with Compiler enabled)
|
|
- **Language**: TypeScript 5.5.4
|
|
- **Styling**: Tailwind CSS 4.1.13 + **shadcn/ui** (new components) / HeroUI 2.8.4 (legacy)
|
|
- **State Management**: Zustand 5.0.8
|
|
- **Authentication**: NextAuth.js 5.0.0-beta.29
|
|
- **Forms/Validation**: React Hook Form 7.62.0 + Zod 4.1.11
|
|
- **AI/Chat**: AI SDK 5.0.59 + @ai-sdk/react 2.0.59
|
|
- **AI Backend**: LangChain @langchain/core 0.3.77 + @ai-sdk/langchain 1.0.59
|
|
- **Charts**: Recharts 2.15.4
|
|
- **Testing**: Playwright 1.53.2
|
|
- **Formatter**: Prettier 3.6.2
|
|
|
|
## Commands
|
|
|
|
### Development
|
|
|
|
```bash
|
|
npm install # Install dependencies
|
|
npm run dev # Start development server (localhost:3000)
|
|
npm run build # Build for production
|
|
npm start # Start production server
|
|
npm run start:standalone # Start standalone server
|
|
```
|
|
|
|
### Code Quality
|
|
|
|
```bash
|
|
npm run typecheck # TypeScript type checking
|
|
npm run lint:check # ESLint checking
|
|
npm run lint:fix # Fix ESLint issues
|
|
npm run format:check # Prettier format checking
|
|
npm run format:write # Format code with Prettier
|
|
npm run healthcheck # Run typecheck + lint together
|
|
```
|
|
|
|
### Testing
|
|
|
|
```bash
|
|
npm run test:e2e # Run Playwright tests
|
|
npm run test:e2e:ui # Run tests with UI
|
|
npm run test:e2e:debug # Debug tests
|
|
npm run test:e2e:headed # Run tests in headed mode
|
|
npm run test:e2e:report # Show test report
|
|
npm run test:e2e:install # Install Playwright browsers
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
ui/
|
|
├── app/ # Next.js App Router
|
|
│ ├── (auth)/ # Authentication pages (sign-in, sign-up)
|
|
│ ├── (prowler)/ # Main application pages
|
|
│ │ ├── compliance/ # Compliance frameworks & reports
|
|
│ │ ├── findings/ # Security findings & vulnerabilities
|
|
│ │ ├── integrations/ # S3, Security Hub integrations
|
|
│ │ ├── lighthouse/ # AI-powered security assistant
|
|
│ │ ├── providers/ # Cloud provider management
|
|
│ │ ├── scans/ # Security scan management
|
|
│ │ └── services/ # Cloud services overview
|
|
│ └── api/ # API routes & server actions
|
|
├── components/ # Reusable UI components
|
|
│ ├── shadcn/ # shadcn/ui components (NEW)
|
|
│ │ ├── card.tsx # shadcn Card component
|
|
│ │ ├── resource-stats-card/ # Custom ResourceStatsCard built on shadcn
|
|
│ │ │ ├── resource-stats-card.tsx
|
|
│ │ │ ├── resource-stats-card.example.tsx
|
|
│ │ │ └── index.ts
|
|
│ │ ├── index.ts # Barrel exports
|
|
│ │ └── README.md
|
|
│ ├── ui/ # Base UI components (buttons, forms, etc.)
|
|
│ ├── compliance/ # Compliance-specific components
|
|
│ ├── findings/ # Findings table & filters
|
|
│ ├── providers/ # Provider management UI
|
|
│ ├── scans/ # Scan management UI
|
|
│ └── integrations/ # Integration configuration
|
|
├── actions/ # Server actions (data fetching/mutations)
|
|
├── lib/ # Utility functions & configurations
|
|
├── types/ # TypeScript type definitions
|
|
├── hooks/ # Custom React hooks
|
|
├── store/ # Zustand state management
|
|
├── tests/ # Playwright E2E tests
|
|
└── styles/ # Global CSS & Tailwind config
|
|
```
|
|
|
|
## shadcn/ui Components
|
|
|
|
### Directory Structure
|
|
|
|
All shadcn/ui based components are located in `components/shadcn/`:
|
|
|
|
```
|
|
shadcn/
|
|
├── card.tsx # shadcn Card component
|
|
├── resource-stats-card/ # Custom ResourceStatsCard built on shadcn
|
|
│ ├── resource-stats-card.tsx
|
|
│ ├── resource-stats-card.example.tsx
|
|
│ └── index.ts
|
|
├── index.ts # Barrel exports
|
|
└── README.md
|
|
```
|
|
|
|
### Usage
|
|
|
|
All shadcn components can be imported from `@/components/shadcn`:
|
|
|
|
```tsx
|
|
import { Card, CardHeader, CardContent } from "@/components/shadcn";
|
|
import { ResourceStatsCard } from "@/components/shadcn";
|
|
```
|
|
|
|
### Adding New shadcn Components
|
|
|
|
When adding new shadcn components using the CLI:
|
|
|
|
```bash
|
|
npx shadcn@latest add [component-name]
|
|
```
|
|
|
|
The component will be automatically added to this directory due to the configuration in `components.json`:
|
|
|
|
```json
|
|
{
|
|
"aliases": {
|
|
"ui": "@/components/shadcn"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Component Guidelines
|
|
|
|
1. **shadcn base components** - Use as-is from shadcn/ui (e.g., `card.tsx`)
|
|
2. **Custom components built on shadcn** - Create in subdirectories (e.g., `resource-stats-card/`)
|
|
3. **CVA variants** - Use Class Variance Authority for type-safe variants
|
|
4. **Theme support** - Include `dark:` classes for dark/light theme compatibility
|
|
5. **TypeScript** - Always export types and use proper typing
|
|
|
|
### Resources
|
|
|
|
- [shadcn/ui Documentation](https://ui.shadcn.com)
|
|
- [CVA Documentation](https://cva.style/docs)
|
|
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)
|
|
|
|
## Key Features
|
|
|
|
### Authentication System
|
|
|
|
- NextAuth.js with multiple providers (credentials, OAuth, SAML)
|
|
- Server-side authentication with middleware protection
|
|
- Session management and role-based access control
|
|
|
|
### Cloud Provider Management
|
|
|
|
- Multi-cloud support (AWS, Azure, GCP, GitHub, K8s, M365)
|
|
- Credential management with secure forms
|
|
- Connection testing and status monitoring
|
|
|
|
### Security Scanning
|
|
|
|
- Real-time scan progress monitoring
|
|
- Bulk operations and filtering
|
|
- Scheduled scan management
|
|
- Download and export capabilities
|
|
|
|
### Compliance Frameworks
|
|
|
|
- 36+ compliance frameworks (CIS, NIST, PCI-DSS, etc.)
|
|
- Interactive compliance reports with charts
|
|
- Requirement-level drill-down views
|
|
- Custom compliance mapping
|
|
|
|
### Lighthouse AI Assistant
|
|
|
|
- LangChain-powered security chatbot
|
|
- Context-aware responses about findings
|
|
- Integration with scan data and compliance frameworks
|
|
|
|
### Findings Management
|
|
|
|
- Advanced filtering and search
|
|
- Mute/unmute functionality with reasons
|
|
- Severity-based classification
|
|
- Detailed finding analysis
|
|
|
|
## Patterns & Conventions
|
|
|
|
### Component Architecture
|
|
|
|
```typescript
|
|
// Prefer server components when possible
|
|
export default async function PageComponent() {
|
|
const data = await fetchData();
|
|
return <ClientComponent data={data} />;
|
|
}
|
|
```
|
|
|
|
### State Management
|
|
|
|
```typescript
|
|
// Use Zustand for global state
|
|
import { useStore } from "@/hooks/use-store";
|
|
|
|
const { filters, setFilters } = useStore();
|
|
```
|
|
|
|
### Server Actions
|
|
|
|
```typescript
|
|
"use server";
|
|
|
|
export async function updateProvider(formData: FormData) {
|
|
// Validate with Zod
|
|
// Update via API
|
|
// Revalidate cache
|
|
}
|
|
```
|
|
|
|
### Form Handling
|
|
|
|
```typescript
|
|
import { useForm } from "react-hook-form";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
|
|
const form = useForm({
|
|
resolver: zodResolver(schema),
|
|
});
|
|
```
|
|
|
|
## File & Code Style
|
|
|
|
### Naming & Organization
|
|
|
|
- **Component Naming**: `PascalCase` for components, `camelCase` for helpers
|
|
- **Foldering**: Colocate domain components under domain folders (e.g., `components/integrations/jira/`)
|
|
- **Imports**: Honor alias paths (`@/components/...`). Keep import order consistent with ESLint rules
|
|
- **CSS**: Prefer Tailwind classes; avoid ad-hoc CSS files unless justified
|
|
|
|
### Import Organization
|
|
|
|
```typescript
|
|
// External libraries
|
|
import React from "react";
|
|
import { Button } from "@heroui/react";
|
|
|
|
// Internal utilities
|
|
import { cn } from "@/lib/utils";
|
|
|
|
// Types
|
|
import type { ComponentProps } from "@/types";
|
|
```
|
|
|
|
## Styling Guidelines
|
|
|
|
### Tailwind + shadcn/ui (New) / HeroUI (Existing)
|
|
|
|
- **Use shadcn/ui components for new UI features/pages** with the new Tailwind theme
|
|
- Existing features/pages should continue using HeroUI (migrated from NextUI) for consistency
|
|
- Custom Prowler color palette defined in tailwind.config.js
|
|
- Dark/light theme support via next-themes
|
|
- Custom shadows and animations for Prowler brand
|
|
|
|
## Library-Specific Guidelines
|
|
|
|
### Zod v4 (Schema Validation)
|
|
|
|
**Breaking changes from v3:**
|
|
|
|
- ❌ `.nonempty()` → ✅ `.min(1)` for strings
|
|
- ❌ `z.string().email()` → ✅ `z.email()` (top-level function)
|
|
- ❌ `z.string().uuid()` → ✅ `z.uuid()` (top-level function)
|
|
- ❌ `z.string().url()` → ✅ `z.url()` (top-level function)
|
|
- ❌ `required_error` parameter → ✅ `error` parameter
|
|
- ❌ `message` parameter → ✅ `error` parameter
|
|
- ⚠️ `.optional()` type inference changed - fields are now `T | undefined` in inferred types
|
|
|
|
**Example migration:**
|
|
|
|
```typescript
|
|
// ❌ Zod v3
|
|
const schema = z.object({
|
|
email: z.string().email({ message: "Invalid email" }),
|
|
name: z.string().nonempty("Required"),
|
|
id: z.string().uuid(),
|
|
});
|
|
|
|
// ✅ Zod v4
|
|
const schema = z.object({
|
|
email: z.email({ error: "Invalid email" }),
|
|
name: z.string().min(1, "Required"),
|
|
id: z.uuid(),
|
|
});
|
|
```
|
|
|
|
### Zustand v5 (State Management)
|
|
|
|
**Breaking changes from v4:**
|
|
|
|
- ✅ No API changes required for basic usage
|
|
- ⚠️ `shallow` comparison must use `useShallow` hook from `zustand/react/shallow`
|
|
- ⚠️ Selectors must return stable references to avoid infinite loops
|
|
- ⚠️ `persist` middleware no longer auto-stores initial state - call `setState()` explicitly if needed
|
|
|
|
**Best practices:**
|
|
|
|
```typescript
|
|
import { create } from "zustand";
|
|
import { persist } from "zustand/middleware";
|
|
|
|
const useStore = create(
|
|
persist(
|
|
(set) => ({
|
|
value: 0,
|
|
increment: () => set((state) => ({ value: state.value + 1 })),
|
|
}),
|
|
{ name: "my-store" },
|
|
),
|
|
);
|
|
```
|
|
|
|
### AI SDK v5 (Chat & AI Features)
|
|
|
|
**Breaking changes from v4:**
|
|
|
|
- ❌ `Message` type → ✅ `UIMessage` type
|
|
- ❌ `message.content` string → ✅ `message.parts` array structure
|
|
- ❌ `handleSubmit` / `handleInputChange` → ✅ `sendMessage` with manual state
|
|
- ❌ `append()` → ✅ `sendMessage({ text: "..." })`
|
|
- ❌ `api: "/endpoint"` → ✅ `transport: new DefaultChatTransport({ api: "/endpoint" })`
|
|
- ❌ `LangChainAdapter.toDataStreamResponse()` → ✅ `toUIMessageStream()` from `@ai-sdk/langchain`
|
|
|
|
**Example migration:**
|
|
|
|
```typescript
|
|
// ❌ AI SDK v4
|
|
import { useChat } from "ai";
|
|
const { messages, handleSubmit, input, handleInputChange } = useChat({
|
|
api: "/api/chat",
|
|
});
|
|
|
|
// ✅ AI SDK v5
|
|
import { useChat } from "@ai-sdk/react";
|
|
import { DefaultChatTransport } from "ai";
|
|
const { messages, sendMessage } = useChat({
|
|
transport: new DefaultChatTransport({ api: "/api/chat" }),
|
|
});
|
|
// Manual input state management required
|
|
const [input, setInput] = useState("");
|
|
const handleSubmit = (e) => {
|
|
e.preventDefault();
|
|
sendMessage({ text: input });
|
|
setInput("");
|
|
};
|
|
```
|
|
|
|
**UIMessage structure:**
|
|
|
|
```typescript
|
|
// Message parts-based structure
|
|
const message: UIMessage = {
|
|
id: "msg-1",
|
|
role: "assistant",
|
|
parts: [{ type: "text", text: "Hello world" }],
|
|
};
|
|
|
|
// Extract text from parts
|
|
const text = message.parts
|
|
.filter((p) => p.type === "text")
|
|
.map((p) => ("text" in p ? p.text : ""))
|
|
.join("");
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Playwright E2E Tests
|
|
|
|
**⚠️ MANDATORY: If you have access to Playwright MCP tools, ALWAYS use them to understand the actual application flow before creating any E2E test.**
|
|
|
|
- **IF Playwright MCP is available**: Use browser tools to navigate, interact, and understand the real UI behavior FIRST, then create tests
|
|
- **IF Playwright MCP is NOT available**: Proceed with test creation based on available documentation and code analysis
|
|
- Add/update E2E tests for critical flows you modify
|
|
- Scope: run only affected specs when iterating
|
|
- Commit snapshot updates only with real UI changes
|
|
- Determinism: avoid relying on real external services; mock or stub where possible
|
|
- **Organization**: Create a folder under `tests/` for each page (e.g., `tests/sign-in/`, `tests/sign-up/`, etc.)
|
|
- **File Structure**: Each page folder should contain 3 files:
|
|
- `{page-name}-page.ts` - Page Object Model
|
|
- `{page-name}.spec.ts` - Test specifications
|
|
- `{page-name}.md` - Test documentation
|
|
- **Base Class**: `tests/base-page.ts` - Parent class that all `{page-name}-page.ts` files should extend
|
|
- **Helpers**: `tests/helpers.ts` - Utility functions and helper methods for tests
|
|
|
|
#### Playwright MCP Integration
|
|
|
|
**⚠️ CRITICAL WORKFLOW (When Available): If you have access to Playwright MCP browser tools, use them to explore the application BEFORE writing any test code.**
|
|
|
|
**Recommended Steps Before Creating Tests (Only if MCP Tools are Available):**
|
|
|
|
1. **Navigate to the application** to reach the target page
|
|
2. **Take a snapshot** to see the page structure and available elements
|
|
3. **Interact with forms and elements** to verify the exact user flow
|
|
4. **Take screenshots** to document expected states at each step
|
|
5. **Verify page transitions** by navigating through the complete flow to understand all states (loading, success, error)
|
|
6. **Document actual selectors** from the snapshots - use the real element references (ref) and labels you observe
|
|
7. **Only after exploring** the complete flow manually, create the test code with the exact selectors and steps you verified
|
|
|
|
**Why This Matters (When MCP Tools are Available):**
|
|
|
|
- ✅ **Precise test creation** - Only include the exact steps needed, no assumptions or guessing
|
|
- ✅ **Accurate selectors** - Use the actual DOM structure from real snapshots, not imagined selectors
|
|
- ✅ **Real flow validation** - Verify the complete user journey actually works as expected
|
|
- ✅ **Avoid over-engineering** - Create minimal tests that focus on what actually exists
|
|
- ✅ **Prevent flaky tests** - Tests based on real exploration are more stable and reliable
|
|
- ❌ **Never assume** - Don't create tests based on assumptions about how the UI "should" work
|
|
|
|
**Benefits:**
|
|
- **Precise test creation** - Only include the exact steps needed for the test requirement
|
|
- **Accurate selectors** - Use the actual DOM structure to create reliable locators
|
|
- **Real flow validation** - Verify the complete user journey works as expected
|
|
- **Avoid over-engineering** - Create minimal tests that focus on the specific requirement
|
|
|
|
#### Test Creation Guidelines
|
|
|
|
**IMPORTANT: Always ask for clarification if the request is ambiguous about scope.**
|
|
|
|
**When creating a specific test:**
|
|
|
|
- Create only a single `test()` entry implementing the specific functionality described
|
|
- Do NOT create the full test suite for this page
|
|
- **ALWAYS add the test to the page's main spec file** (e.g., `sign-up.spec.ts`), NOT in a separate file
|
|
- **REUSE existing page objects** from other pages when possible (e.g., use existing SignInPage, HomePage, etc.)
|
|
- If the page's spec file doesn't exist, create minimal structure:
|
|
- `{page-name}-page.ts` - Page Object Model
|
|
- `{page-name}.spec.ts` - Test specifications (add your specific test here)
|
|
- Focus on the exact requirement without additional test cases
|
|
- Do NOT create separate files like `{page-name}-critical-path.spec.ts` or `{page-name}-specific-test.spec.ts`
|
|
|
|
**When creating comprehensive page tests:**
|
|
|
|
- Create the full test suite with all files (page object, spec, documentation)
|
|
- Include multiple test cases covering various scenarios in `{page-name}.spec.ts`
|
|
- Follow the complete structure with validation, error handling, accessibility tests
|
|
- Create comprehensive documentation for all test cases in `{page-name}.md`
|
|
|
|
**File Naming Convention:**
|
|
|
|
- ✅ **CORRECT**: `sign-up.spec.ts` (contains all sign-up tests)
|
|
- ✅ **CORRECT**: `sign-up-page.ts` (page object)
|
|
- ✅ **CORRECT**: `sign-up.md` (documentation for all tests)
|
|
- ❌ **WRONG**: `sign-up-critical-path.spec.ts` (separate file for specific test)
|
|
- ❌ **WRONG**: `sign-up-validation.spec.ts` (separate file for specific test)
|
|
|
|
**Examples:**
|
|
|
|
```typescript
|
|
// ✅ Specific test request - create only this test
|
|
test("User can create account and login successfully",{
|
|
tag: ['@critical', '@e2e', '@signup', '@SIGNUP-E2E-001']
|
|
} async ({ page }) => {
|
|
// Implementation for this specific test only
|
|
});
|
|
|
|
// ❌ Don't create full suite when only one test is requested
|
|
```
|
|
|
|
**Request Examples:**
|
|
|
|
- **"Create a test for user sign-up"** → Create only the sign-up test, not the full suite
|
|
- **"Generate E2E tests for the login page"** → Create comprehensive test suite with all scenarios
|
|
- **"Add a test to verify form validation"** → Add only the validation test to existing spec
|
|
- **"Create tests for the home page"** → Create full test suite for home functionality
|
|
- **"Create a new test e2e for sign-up"** → Create only the specific test mentioned
|
|
- **"Generate comprehensive E2E tests for sign-up"** → Create full test suite
|
|
|
|
**Key Phrases to Identify Scope:**
|
|
|
|
- **Single Test**: "a test", "one test", "new test", "add test"
|
|
- **Full Suite**: "comprehensive tests", "all tests", "test suite", "complete tests", "generate tests"
|
|
|
|
#### Page Object Model Pattern
|
|
|
|
- **Extend BasePage**: All page objects should extend `BasePage` for common functionality
|
|
- **REUSE Existing Page Objects**: Always check for existing page objects before creating new ones
|
|
- **Interface Definitions**: Define clear interfaces for form data and credentials
|
|
- **Method Organization**: Group methods by functionality (navigation, form interaction, validation, etc.)
|
|
- **Locator Strategy**: Use stable selectors (name attributes, labels) over fragile CSS selectors
|
|
- **Avoid Code Duplication**: When creating a new page object, verify if there are repeated methods across page objects that should be moved to `BasePage`
|
|
- **Shared Utilities**: If utility functions are repeated across tests, create or update `tests/helpers.ts` to centralize them
|
|
- **Refactor to BasePage**: Common patterns like form validation, notification checks, or navigation should be extracted to `BasePage`
|
|
- **Refactor to Helpers**: Data generation, test setup utilities, or common assertions should be extracted to `tests/helpers.ts`
|
|
|
|
#### Page Object Reuse Guidelines
|
|
|
|
- **Check existing page objects first**: Look in `tests/` directory for existing page objects
|
|
- **Import and reuse**: Use existing page objects like `SignInPage`, `HomePage`, etc.
|
|
- **Create page objects when needed**: If a test requires interaction with a page that doesn't have a page object yet, create it following the Page Object Model pattern
|
|
- **Only create new page objects** when the page doesn't exist or has unique functionality
|
|
- **Example**: For a sign-up test that needs to verify login after signup, reuse `SignInPage` and `HomePage` if they exist, or create them if needed
|
|
- **Avoid duplication**: Don't recreate functionality that already exists in other page objects
|
|
- **Complete dependencies**: When creating a test that requires multiple page interactions, ensure all necessary page objects exist (create them if they don't)
|
|
|
|
#### Code Refactoring Guidelines
|
|
|
|
**When to move code to `BasePage`:**
|
|
|
|
- ✅ **Navigation helpers** used by multiple pages (e.g., `waitForPageLoad()`, `getCurrentUrl()`)
|
|
- ✅ **Common UI interactions** (e.g., clicking notifications, handling modals, theme toggles)
|
|
- ✅ **Verification patterns** repeated across pages (e.g., `isVisible()`, `waitForVisible()`)
|
|
- ✅ **Error handling** that applies to all pages
|
|
- ✅ **Screenshot utilities** for debugging
|
|
|
|
**When to move code to `tests/helpers.ts`:**
|
|
|
|
- ✅ **Test data generation** (e.g., `generateUniqueEmail()`, `generateTestUser()`)
|
|
- ✅ **Setup/teardown utilities** (e.g., `createTestUser()`, `cleanupTestData()`)
|
|
- ✅ **Custom assertions** used across tests (e.g., `expectNotificationToContain()`)
|
|
- ✅ **API helpers** for test setup (e.g., `seedDatabase()`, `resetState()`)
|
|
- ✅ **Time utilities** (e.g., `waitForCondition()`, `retryAction()`)
|
|
|
|
**Example - Before Refactoring:**
|
|
|
|
```typescript
|
|
// ❌ BAD: Repeated code in multiple page objects
|
|
export class SignUpPage extends BasePage {
|
|
async waitForNotification(): Promise<void> {
|
|
await this.page.waitForSelector('[role="status"]');
|
|
}
|
|
}
|
|
|
|
export class SignInPage extends BasePage {
|
|
async waitForNotification(): Promise<void> {
|
|
await this.page.waitForSelector('[role="status"]');
|
|
}
|
|
}
|
|
```
|
|
|
|
**Example - After Refactoring:**
|
|
|
|
```typescript
|
|
// ✅ GOOD: Move to BasePage
|
|
export class BasePage {
|
|
async waitForNotification(): Promise<void> {
|
|
await this.page.waitForSelector('[role="status"]');
|
|
}
|
|
|
|
async verifyNotificationMessage(message: string): Promise<void> {
|
|
const notification = this.page.locator('[role="status"]');
|
|
await expect(notification).toContainText(message);
|
|
}
|
|
}
|
|
|
|
// ✅ GOOD: Move to helpers.ts for data generation
|
|
export function generateUniqueEmail(): string {
|
|
const timestamp = Date.now();
|
|
return `test.user.${timestamp}@example.com`;
|
|
}
|
|
|
|
export function generateTestUser() {
|
|
return {
|
|
name: "Test User",
|
|
email: generateUniqueEmail(),
|
|
password: "TestPassword123!",
|
|
};
|
|
}
|
|
```
|
|
|
|
**Page Object Reuse Example:**
|
|
|
|
```typescript
|
|
// ✅ GOOD: Check for existing page objects, create if needed
|
|
// 1. Check if SignInPage exists - if not, create it
|
|
// 2. Check if HomePage exists - if not, create it
|
|
import { SignInPage } from "../sign-in/sign-in-page";
|
|
import { HomePage } from "../home/home-page";
|
|
|
|
test("User can sign up and login", async ({ page }) => {
|
|
const signUpPage = new SignUpPage(page);
|
|
const signInPage = new SignInPage(page); // REUSE existing (or create if missing)
|
|
const homePage = new HomePage(page); // REUSE existing (or create if missing)
|
|
|
|
// Use existing functionality
|
|
await signUpPage.signUp(userData);
|
|
await homePage.verifyPageLoaded(); // REUSE existing method
|
|
await homePage.signOut(); // REUSE existing method
|
|
await signInPage.login(credentials); // REUSE existing method
|
|
});
|
|
|
|
// ❌ BAD: Don't recreate existing functionality in SignUpPage
|
|
export class SignUpPage extends BasePage {
|
|
// Don't recreate logout functionality
|
|
async logout() {
|
|
/* ... */
|
|
} // ❌ HomePage already has this
|
|
|
|
// Don't recreate login functionality
|
|
async login() {
|
|
/* ... */
|
|
} // ❌ SignInPage already has this
|
|
|
|
// ✅ GOOD: Instead, use composition or delegation
|
|
async loginAfterSignUp(credentials: LoginCredentials): Promise<void> {
|
|
// Reuse SignInPage methods or delegate to it
|
|
const emailField = this.page.getByRole("textbox", { name: "Email*" });
|
|
const passwordField = this.page.getByRole("textbox", { name: "Password*" });
|
|
const loginButton = this.page.getByRole("button", { name: "Log in" });
|
|
|
|
await emailField.fill(credentials.email);
|
|
await passwordField.fill(credentials.password);
|
|
await loginButton.click();
|
|
}
|
|
}
|
|
```
|
|
|
|
**Page Object Structure:**
|
|
|
|
```typescript
|
|
export interface FeatureData {
|
|
email: string;
|
|
password: string;
|
|
// ... other fields
|
|
}
|
|
|
|
export class FeaturePage extends BasePage {
|
|
// Form elements
|
|
readonly emailInput: Locator;
|
|
readonly passwordInput: Locator;
|
|
readonly submitButton: Locator;
|
|
|
|
constructor(page: Page) {
|
|
super(page);
|
|
// Use stable selectors
|
|
this.emailInput = page.getByLabel("Email");
|
|
this.passwordInput = page.locator('input[name="password"]');
|
|
this.submitButton = page.getByRole("button", { name: "Submit" });
|
|
}
|
|
|
|
async goto(): Promise<void> {
|
|
await super.goto("/feature-path");
|
|
}
|
|
|
|
async performAction(data: FeatureData): Promise<void> {
|
|
await this.emailInput.fill(data.email);
|
|
await this.passwordInput.fill(data.password);
|
|
await this.submitButton.click();
|
|
}
|
|
|
|
async verifyCriticalOutcome(): Promise<void> {
|
|
await expect(this.page).toHaveURL("/expected-path");
|
|
// ... verification logic
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Test Structure Best Practices
|
|
|
|
- **Page Object Usage**: Use Page Object Models for all page interactions
|
|
- **Tag Organization**: Use Playwright tag syntax for test categorization
|
|
- **Test IDs**: Include test case IDs in tags for traceability
|
|
- **Verification Steps**: Include clear verification steps for each major action
|
|
|
|
**Key Elements:**
|
|
|
|
- **Page Objects**: All interactions through Page Object Models
|
|
- **Clear Tags**: Use `{ tag: ['@priority', '@type', '@feature', '@test-id'] }` syntax
|
|
- **Verification**: Explicit verification of critical outcomes
|
|
|
|
#### Playwright Selector Best Practices
|
|
|
|
When creating locators in Page Objects, follow this priority order for maximum reliability:
|
|
|
|
**✅ Primary Selectors (Recommended):**
|
|
|
|
- **`getByRole()`**: The best and most robust for all interactive elements (buttons, links, main sections)
|
|
- **`getByLabel()`**: The best for form controls that have an associated label
|
|
|
|
**⚠️ Secondary Selectors (Use Sparingly):**
|
|
|
|
- **`getByText()`**: Use only when the above fail or for static text verification (headings, paragraphs, messages)
|
|
- **Others (e.g. `getByTestId()`)**: Use only as a last resort when the above fail or are not applicable
|
|
|
|
**Examples:**
|
|
|
|
```typescript
|
|
// ✅ GOOD - Using getByRole for interactive elements
|
|
this.submitButton = page.getByRole("button", { name: "Submit" });
|
|
this.navigationLink = page.getByRole("link", { name: "Dashboard" });
|
|
|
|
// ✅ GOOD - Using getByLabel for form controls
|
|
this.emailInput = page.getByLabel("Email");
|
|
this.passwordInput = page.getByLabel("Password");
|
|
|
|
// ⚠️ SPARINGLY - Using getByText only when necessary
|
|
this.errorMessage = page.getByText("Invalid credentials"); // Only if no better selector exists
|
|
this.pageTitle = page.getByText("Welcome to Prowler"); // Only for static content verification
|
|
|
|
// ❌ AVOID - Using fragile selectors when better options exist
|
|
this.submitButton = page.locator(".btn-primary"); // Use getByRole instead
|
|
this.emailInput = page.locator("#email"); // Use getByLabel instead
|
|
```
|
|
|
|
**Tag Syntax Example:**
|
|
|
|
```typescript
|
|
test(
|
|
"Test description",
|
|
{ tag: ["@critical", "@e2e", "@signup", "@SIGNUP-E2E-001"] },
|
|
async ({ page }) => {
|
|
// Test implementation
|
|
},
|
|
);
|
|
```
|
|
|
|
#### E2E Test Documentation Format
|
|
|
|
Each test documentation file (`{page-name}.md`) should follow this structured format:
|
|
|
|
```markdown
|
|
### E2E Tests: {Feature Name}
|
|
|
|
**Suite ID:** `{SUITE-ID}`
|
|
**Feature:** {Feature description}
|
|
|
|
---
|
|
|
|
## Test Case: `{TEST-ID}` - {Test case title}
|
|
|
|
**Priority:** `{critical|high|medium|low}`
|
|
|
|
**Tags:**
|
|
|
|
- type → @e2e
|
|
- feature → @{feature-name}
|
|
|
|
**Description/Objective:** {Brief description of what the test validates}
|
|
|
|
**Preconditions:**
|
|
|
|
- {List of prerequisites for the test to run}
|
|
- {Any required data or state}
|
|
|
|
### Flow Steps:
|
|
|
|
1. {Step 1 description}
|
|
2. {Step 2 description}
|
|
3. {Step 3 description}
|
|
...
|
|
|
|
### Expected Result:
|
|
|
|
- {Expected outcome 1}
|
|
- {Expected outcome 2}
|
|
...
|
|
|
|
### Key verification points:
|
|
|
|
- {Key assertion 1}
|
|
- {Key assertion 2}
|
|
- {Key assertion 3}
|
|
|
|
### Notes:
|
|
|
|
- {Any additional notes or considerations}
|
|
- {Test data requirements or constraints}
|
|
```
|
|
|
|
#### Test Documentation Best Practices
|
|
- **Suite ID Format**: Use descriptive suite IDs (e.g., `SIGNUP-E2E`)
|
|
- **Test ID Format**: Include feature and sequence (e.g., `SIGNUP-E2E-001`)
|
|
- **Priority Levels**: Use `critical`, `high`, `medium`, `low` for test prioritization
|
|
- **Tag Organization**: Use Playwright tag syntax: `{ tag: ['@priority', '@type', '@feature', '@test-id'] }`
|
|
- **Flow Steps**: Number steps clearly and describe user actions
|
|
- **Verification Points**: List specific assertions and expected outcomes
|
|
- **Preconditions**: Document any required setup or data dependencies
|
|
- **Test Data Notes**: Include information about data generation and uniqueness strategies
|
|
|
|
**Tag Categories:**
|
|
- **Priority**: `@critical`, `@high`, `@medium`, `@low`
|
|
- **Type**: `@e2e`
|
|
- **Feature**: `@signup`, `@signin`, `@dashboard`
|
|
- **Test ID**: `@SIGNUP-E2E-001`, `@LOGIN-E2E-002`
|
|
|
|
**IMPORTANT - Keep Documentation Concise:**
|
|
- ❌ **DO NOT** include general test running instructions
|
|
- ❌ **DO NOT** include file structure explanations
|
|
- ❌ **DO NOT** include code examples or tutorials
|
|
- ❌ **DO NOT** include extensive troubleshooting sections
|
|
- ❌ **DO NOT** include command references or configuration details
|
|
- ✅ **DO** focus only on the specific test case: flow, preconditions, expected results, and verification points
|
|
- ✅ **DO** keep the documentation under 60 lines when possible
|
|
- ✅ **DO** follow the exact format template provided above
|
|
|
|
|
|
### Component Testing (Future)
|
|
- Jest + React Testing Library
|
|
- Component unit tests
|
|
- Integration tests for complex flows
|
|
|
|
## Performance
|
|
|
|
- Keep Client Components lean; avoid heavy client-side logic where server boundary is possible
|
|
- Use streaming, partial rendering, and skeletons for long operations
|
|
- **DO NOT use `useMemo` or `useCallback`** - React 19 Compiler handles this automatically
|
|
- App Router with server components
|
|
- Image optimization with next/image
|
|
- Font optimization with next/font
|
|
- Bundle analysis and code splitting
|
|
|
|
## Security
|
|
|
|
### Best Practices
|
|
|
|
- Do not commit secrets or `.env.local` values. Use placeholders in examples
|
|
- Avoid logging sensitive data. Sanitize error messages shown to users
|
|
- Strict CSP headers in next.config.js
|
|
- XSS protection and CSRF mitigation
|
|
- Secure cookie configuration
|
|
|
|
### Data Handling
|
|
|
|
- Client-side validation with Zod
|
|
- Server-side sanitization
|
|
- Secure credential storage patterns
|
|
|
|
### Authentication
|
|
|
|
- NextAuth: use server helpers and `server-only` where appropriate
|
|
- Protect routes through middleware and layout boundaries
|
|
- JWT tokens via NextAuth
|
|
- Automatic token refresh
|
|
- Protected routes via middleware
|
|
|
|
## Quality Gates (before submitting changes) IMPORTANT!
|
|
|
|
1. `npm run typecheck` shows 0 new errors
|
|
2. `npm run lint:check` passes or is fixed via `npm run lint:fix`
|
|
3. `npm run format:check` passes or is corrected via `npm run format:write`
|
|
4. Relevant Playwright specs pass locally
|
|
5. UI states (loading, error, empty) are handled
|
|
|
|
## Common Development Tasks
|
|
|
|
### Adding New Pages
|
|
|
|
1. Create page component in `app/(prowler)/`
|
|
2. Add route to navigation in `lib/menu-list.ts`
|
|
3. Implement required server actions
|
|
4. Add proper TypeScript types (using const-based type pattern). IMPORTANT!: avoid "any" type.
|
|
|
|
### Creating Components
|
|
|
|
1. **Use shadcn/ui for new UI components that belong to new features/pages**
|
|
2. Existing features/pages should continue using HeroUI for consistency
|
|
3. Follow established patterns in `components/shadcn/` for new components
|
|
4. Implement proper TypeScript interfaces (using const-based type pattern)
|
|
5. Add to component index files
|
|
|
|
### Integrating with Backend
|
|
|
|
1. Create server actions in `actions/`
|
|
2. Define TypeScript types in `types/` (using const-based type pattern)
|
|
3. Handle loading and error states
|
|
4. Implement proper caching strategy
|
|
|
|
### When Implementing New UI
|
|
|
|
- **Use shadcn/ui components with the new Tailwind theme for new UI features/pages**
|
|
- **For existing features/pages, continue using HeroUI components for consistency**
|
|
- Start from existing patterns in the closest domain folder
|
|
- Reuse primitives from `components/shadcn` (for new features) or `components/ui` (HeroUI for existing) and existing composables
|
|
- Add types to `types/` if they're shared (2+ features); otherwise colocate types near usage (1 feature)
|
|
- Follow The Scope Rule strictly - used by 2+ features = shared, 1 feature = local
|
|
- Update feature docs and `ui/CHANGELOG.md` when behavior changes
|
|
|
|
### Documentation Links Pattern (Integrations Only)
|
|
|
|
For integration features (e.g., API Keys, SAML, S3) that have dedicated documentation, include "Read the docs" links in both the main card/component and related modals:
|
|
|
|
**Main Card Header** (e.g., API Keys card):
|
|
```tsx
|
|
<p className="text-xs text-gray-500">
|
|
Manage API keys for programmatic access.{" "}
|
|
<CustomLink href="https://docs.prowler.com/user-guide/providers/prowler-app-api-keys">
|
|
Read the docs
|
|
</CustomLink>
|
|
</p>
|
|
```
|
|
|
|
**Modal/Form** (e.g., Create API Key modal):
|
|
```tsx
|
|
<p className="text-xs text-gray-500">
|
|
Need help configuring API Keys?{" "}
|
|
<CustomLink href="https://docs.prowler.com/user-guide/providers/prowler-app-api-keys">
|
|
Read the docs
|
|
</CustomLink>
|
|
</p>
|
|
```
|
|
|
|
**Rules:**
|
|
- **Only apply to integration components** (API Keys, SAML, S3, Security Hub, etc.)
|
|
- Use the same documentation URL across related components
|
|
- Tailor the helper text to the component context (card = general, modal = action-specific)
|
|
- Apply `text-xs text-gray-500` styling for consistency
|
|
- Place the link in a `<p>` tag or description area within the component
|
|
- Always use `CustomLink` component for documentation links
|
|
|
|
## Environment Configuration
|
|
|
|
### Required Environment Variables
|
|
|
|
```bash
|
|
# Authentication
|
|
NEXTAUTH_SECRET=your_secret_here
|
|
NEXTAUTH_URL=http://localhost:3000
|
|
|
|
# API Configuration
|
|
NEXT_PUBLIC_API_BASE_URL=http://localhost:8080
|
|
|
|
# AI Features
|
|
OPENAI_API_KEY=your_openai_key
|
|
```
|
|
|
|
### Development Setup
|
|
|
|
1. Copy `.env.example` to `.env.local`
|
|
2. Configure authentication providers
|
|
3. Set up API backend connection
|
|
4. Install dependencies and start dev server
|
|
|
|
## Deployment
|
|
|
|
### Production Build
|
|
|
|
```bash
|
|
npm run build # Build optimized bundle
|
|
npm run start # Start production server
|
|
```
|
|
|
|
### Docker Deployment
|
|
|
|
- Dockerfile available for containerization
|
|
- Standalone output for minimal container size
|
|
- Health checks via `/api/health` endpoint
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **TypeScript errors**: Run `npm run typecheck`
|
|
2. **Lint issues**: Run `npm run lint:fix`
|
|
3. **Build failures**: Check Next.js build logs
|
|
4. **Authentication issues**: Verify NextAuth configuration
|
|
|
|
### Debug Tools
|
|
|
|
- Next.js built-in debugger
|
|
- React Developer Tools
|
|
- Network tab for API debugging
|
|
- Lighthouse for performance analysis
|
|
|
|
## Recent Major Migrations (January 2025)
|
|
|
|
- ✅ React 18 → 19.1.1 (async components, useActionState, React Compiler)
|
|
- ✅ Next.js 14 → 15.5.3 (enhanced App Router)
|
|
- ✅ NextUI → HeroUI 2.8.4
|
|
- ✅ Zod 3.25.73 → 4.1.11 (breaking: deprecated methods)
|
|
- ✅ Zustand 4.5.7 → 5.0.8 (compatible)
|
|
- ✅ AI SDK 4.3.16 → 5.0.59 (breaking: new message structure)
|
|
- ✅ LangChain updates with new adapter patterns
|
|
|
|
## Summary
|
|
|
|
These guidelines ensure:
|
|
|
|
- ✅ Consistent code patterns across the project
|
|
- ✅ Optimal performance with Next.js 15 and React 19 with Compiler
|
|
- ✅ Clear, maintainable architecture following The Scope Rule
|
|
- ✅ Proper separation of concerns
|
|
- ✅ Type safety throughout the codebase using const-based types
|
|
- ✅ Semantic Tailwind usage without var() or hex colors in className
|
|
- ✅ Purposeful use of cn() only for conditionals and merging
|
|
|
|
**When in doubt, ask before deviating from these patterns.**
|
|
|
|
## References
|
|
|
|
- **High-level project guide**: `../AGENTS.md` (root Prowler project - takes priority)
|
|
- **UI Changelog**: `./CHANGELOG.md`
|