prowler-cloud

prowler-ui

Prowler UI-specific patterns. For generic patterns, see: typescript, react-19, nextjs-15, tailwind-4. Trigger: When working inside ui/ on Prowler-specific conventions (shadcn vs HeroUI legacy, folder placement, actions/adapters, shared types/hooks/lib).

prowler-cloud 15,090 2,453 Updated 9mo ago

Resources

1
GitHub

Install

npx skillscat add prowler-cloud/prowler/prowler-ui

Install via the SkillsCat registry.

SKILL.md

Related Generic Skills

  • typescript - Const types, flat interfaces
  • react-19 - No useMemo/useCallback, compiler
  • nextjs-15 - App Router, Server Actions
  • tailwind-4 - cn() utility, styling rules
  • zod-4 - Schema validation
  • zustand-5 - State management
  • ai-sdk-5 - Chat/AI features
  • playwright - E2E testing (see also prowler-test-ui)

Tech Stack (Versions)

Next.js 15.5.9 | React 19.2.2 | Tailwind 4.1.13 | shadcn/ui
Zod 4.1.11 | React Hook Form 7.62.0 | Zustand 5.0.8
NextAuth 5.0.0-beta.30 | Recharts 2.15.4
HeroUI 2.8.4 (LEGACY - do not add new components)

CRITICAL: Component Library Rule

  • ALWAYS: Use shadcn/ui + Tailwind (components/shadcn/)
  • NEVER: Add new HeroUI components (components/ui/ is legacy only)

DECISION TREES

Component Placement

New feature UI? โ†’ shadcn/ui + Tailwind
Existing HeroUI feature? โ†’ Keep HeroUI (don't mix)
Used 1 feature? โ†’ features/{feature}/components/
Used 2+ features? โ†’ components/shared/
Needs state/hooks? โ†’ "use client"
Server component? โ†’ No directive needed

Code Location

Server action      โ†’ actions/{feature}/{feature}.ts
Data transform     โ†’ actions/{feature}/{feature}.adapter.ts
Types (shared 2+)  โ†’ types/{domain}.ts
Types (local 1)    โ†’ {feature}/types.ts
Utils (shared 2+)  โ†’ lib/
Utils (local 1)    โ†’ {feature}/utils/
Hooks (shared 2+)  โ†’ hooks/
Hooks (local 1)    โ†’ {feature}/hooks.ts
shadcn components  โ†’ components/shadcn/
HeroUI components  โ†’ components/ui/ (LEGACY)

Styling Decision

Tailwind class exists? โ†’ className
Dynamic value?         โ†’ style prop
Conditional styles?    โ†’ cn()
Static only?           โ†’ className (no cn())
Recharts/library?      โ†’ CHART_COLORS constant + var()

Scope Rule (ABSOLUTE)

  • Used 2+ places โ†’ lib/ or types/ or hooks/ (components go in components/{domain}/)
  • Used 1 place โ†’ keep local in feature directory
  • This determines ALL folder structure decisions

Project Structure

ui/
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ (auth)/              # Auth pages (login, signup)
โ”‚   โ””โ”€โ”€ (prowler)/           # Main app
โ”‚       โ”œโ”€โ”€ compliance/
โ”‚       โ”œโ”€โ”€ findings/
โ”‚       โ”œโ”€โ”€ providers/
โ”‚       โ”œโ”€โ”€ scans/
โ”‚       โ”œโ”€โ”€ services/
โ”‚       โ””โ”€โ”€ integrations/
โ”œโ”€โ”€ components/
โ”‚   โ”œโ”€โ”€ shadcn/              # shadcn/ui (USE THIS)
โ”‚   โ”œโ”€โ”€ ui/                  # HeroUI (LEGACY)
โ”‚   โ”œโ”€โ”€ {domain}/            # Domain-specific (compliance, findings, providers, etc.)
โ”‚   โ”œโ”€โ”€ filters/             # Filter components
โ”‚   โ”œโ”€โ”€ graphs/              # Chart components
โ”‚   โ””โ”€โ”€ icons/               # Icon components
โ”œโ”€โ”€ actions/                 # Server actions
โ”œโ”€โ”€ types/                   # Shared types
โ”œโ”€โ”€ hooks/                   # Shared hooks
โ”œโ”€โ”€ lib/                     # Utilities
โ”œโ”€โ”€ store/                   # Zustand state
โ”œโ”€โ”€ tests/                   # Playwright E2E
โ””โ”€โ”€ styles/                  # Global CSS

Recharts (Special Case)

For Recharts props that don't accept className:

const CHART_COLORS = {
  primary: "var(--color-primary)",
  secondary: "var(--color-secondary)",
  text: "var(--color-text)",
  gridLine: "var(--color-border)",
};

// Only use var() for library props, NEVER in className
<XAxis tick={{ fill: CHART_COLORS.text }} />
<CartesianGrid stroke={CHART_COLORS.gridLine} />

Form + Validation Pattern

"use client";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";

const schema = z.object({
  email: z.email(),  // Zod 4 syntax
  name: z.string().min(1),
});

type FormData = z.infer<typeof schema>;

export function MyForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
    resolver: zodResolver(schema),
  });

  const onSubmit = async (data: FormData) => {
    await serverAction(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email")} />
      {errors.email && <span>{errors.email.message}</span>}
      <button type="submit">Submit</button>
    </form>
  );
}

Commands

# Development
cd ui && pnpm install
cd ui && pnpm run dev

# Code Quality
cd ui && pnpm run typecheck
cd ui && pnpm run lint:fix
cd ui && pnpm run format:write
cd ui && pnpm run healthcheck    # typecheck + lint

# Testing
cd ui && pnpm run test:e2e
cd ui && pnpm run test:e2e:ui
cd ui && pnpm run test:e2e:debug

# Build
cd ui && pnpm run build
cd ui && pnpm start

QA Checklist Before Commit

  • pnpm run typecheck passes
  • pnpm run lint:fix passes
  • pnpm run format:write passes
  • Relevant E2E tests pass
  • All UI states handled (loading, error, empty)
  • No secrets in code (use .env.local)
  • Error messages sanitized (no stack traces to users)
  • Server-side validation present (don't trust client)
  • Accessibility: keyboard navigation, ARIA labels
  • Mobile responsive (if applicable)

Migrations Reference

From To Key Changes
React 18 19.1 Async components, React Compiler (no useMemo/useCallback)
Next.js 14 15.5 Improved App Router, better streaming
NextUI HeroUI 2.8.4 Package rename only, same API
Zod 3 4 z.email() not z.string().email(), error not message
AI SDK 4 5 @ai-sdk/react, sendMessage not handleSubmit, parts not content

Resources

  • Documentation: See references/ for links to local developer guide