npm package: @hero-dynamic-form/client
Description: React components library for Hero Dynamic Forms with Tailwind CSS support.
Author: hero-truong
Latest version: 1.0.51

# @hero-dynamic-form/client

React components library for Hero Dynamic Forms with Tailwind CSS support.

## Installation

```bash
npm install @hero-dynamic-form/client
# or
pnpm add @hero-dynamic-form/client
# or
yarn add @hero-dynamic-form/client
```

## Setup

This library uses **Tailwind CSS Preset approach** (Solution 3), which means:
- Components are exported with Tailwind classes (NOT pre-compiled CSS)
- Consumer projects can **customize the theme** via their own `tailwind.config.js`
- Tailwind will scan and generate CSS at build time

### 1. Configure Tailwind CSS

In your project's `tailwind.config.js`:

```javascript
const colors = require('tailwindcss/colors');

module.exports = {
  // Import preset to get theme (colors, fonts, etc.)
  presets: [
    require('@hero-dynamic-form/client/tailwind-preset')
  ],

  // ⚠️ IMPORTANT: Must include package dist in content paths
  // Tailwind needs to scan package components for classes
  content: [
    './src/**/*.{js,ts,jsx,tsx}',
    './node_modules/@hero-dynamic-form/client/dist/**/*.js'
  ],

  // Optional: Override or extend theme
  theme: {
    extend: {
      colors: {
        // Example: Change primary color
        primary: colors.red,  // Instead of default indigo
      },
    },
  },
}
```

**Why this works:**
- ✅ **Preset provides theme** - Custom colors (`primary`, `bodydark`, etc.), fonts, fontSize
- ✅ **Preset provides plugins** - `@tailwindcss/forms` and `@tailwindcss/typography` included
- ✅ **You control content paths** - Scan your source + package dist
- ✅ **You can override** - Extend or override any theme values

**Without Preset (Not Recommended):**

If you don't want to use the preset, you must manually copy all theme configuration (50+ lines of colors, fontSize, etc.) and add plugins yourself.

### 2. Import Components

```typescript
import { Button, Card } from '@hero-dynamic-form/client';

function App() {
  return (
    <div>
      <Button variant="primary" size="md">
        Click me
      </Button>

      <Card title="My Card">
        Card content here
      </Card>
    </div>
  );
}
```

## Why Use Preset?

### Benefits of Using Preset

✅ **Get theme automatically** - No need to copy-paste complex config

✅ **Easy to override** - Only override values you want to change

✅ **Always in sync** - When package updates theme, you automatically get updates

✅ **Simplified config** - Your config file is short and maintainable

✅ **Plugins included** - `@tailwindcss/forms` and `@tailwindcss/typography` ready

### Preset vs Manual Config

| Feature | With Preset | Manual Config |
|---------|-------------|---------------|
| Config complexity | 🟢 Simple (~10 lines) | 🔴 Complex (~60+ lines) |
| Theme values | 🟢 Auto included | 🔴 Must copy manually |
| Theme sync | 🟢 Auto sync | 🔴 Manual updates |
| Override colors | 🟢 Override only what you need | 🔴 Define everything |
| Plugins | 🟢 Included | 🔴 Must add manually |

## How It Works

### Tailwind CSS Configuration Strategy

This package uses **Giải pháp 3: Tailwind Plugin/Preset** approach:

1. **Components are NOT pre-compiled** - They export React components with Tailwind classes like `bg-primary-600`, `text-white`, etc.

2. **Consumer's Tailwind scans the package** - Via the `content` configuration, Tailwind will scan `node_modules/@hero-dynamic-form/client/**/*.{js,tsx}` to find all Tailwind classes used.

3. **CSS is generated at consumer's build time** - When consumer runs their build, Tailwind generates CSS based on:
   - Classes found in consumer's code
   - Classes found in `@hero-dynamic-form/client` components
   - Theme configuration from consumer's `tailwind.config.js`

### Benefits

✅ **Consumer can customize theme** - Change colors, fonts, spacing, etc. via their own config

✅ **Automatic CSS purging** - Only CSS for classes actually used will be generated

✅ **No CSS bloat** - No pre-compiled CSS file included in the package

✅ **Consistent with existing patterns** - Same approach as `@hero-dynamic-form/core`

### Example: Changing Primary Color

If consumer changes their config:

```javascript
// example/tailwind.config.js
theme: {
  extend: {
    colors: {
      primary: colors.red,  // Changed from indigo to red
    }
  }
}
```

All components using `bg-primary-600`, `text-primary-500`, etc. will automatically use **red colors** instead of indigo!

**Proof:**
```css
/* Before (with primary: colors.indigo) */
.bg-primary-600 {
  background-color: rgb(79 70 229);  /* Indigo */
}

/* After (with primary: colors.red) */
.bg-primary-600 {
  background-color: rgb(220 38 38);  /* Red */
}
```

## Components

### Button

```typescript
import { Button } from '@hero-dynamic-form/client';

<Button variant="primary" size="md" onClick={() => console.log('clicked')}>
  Click me
</Button>
```

**Props:**
- `variant?: 'primary' | 'secondary' | 'danger'` - Button style variant
- `size?: 'sm' | 'md' | 'lg'` - Button size
- `children: React.ReactNode` - Button content
- All standard HTML button attributes

### Card

```typescript
import { Card } from '@hero-dynamic-form/client';

<Card title="Card Title" className="mt-4">
  <p>Card content goes here</p>
</Card>
```

**Props:**
- `title?: string` - Optional card title
- `children: React.ReactNode` - Card content
- `className?: string` - Additional CSS classes

## Toast Notifications

Built-in toast notifications using `react-toastify`:

```typescript
import { toast } from '@hero-dynamic-form/client';

// Success notification
toast.success('Created', 'Record has been created successfully');

// Error notification
toast.error('Error', 'Something went wrong');

// Info notification
toast.info('Info', 'FYI message');

// Warning notification
toast.warning('Warning', 'Be careful');

// Custom toast with component
toast.custom(<MyCustomComponent />);
```

Toast is automatically included in the `Application` component. To customize:

```typescript
<Application
  schemas={schemas}
  clientConfig={config}
  uiConfig={{
    toast: {
      position: 'top-right',    // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
      autoClose: 3000,          // milliseconds or false
      theme: 'colored',         // 'light' | 'dark' | 'colored'
    }
  }}
/>
```

## UI Configuration & Icon Overrides

The `UIProvider` allows customization of icons and UI elements. Icons can be overridden at multiple levels:

### Icon Override Priority

```
defaults < schemaIcons < uiConfig.icons < runtime
```

### Built-in Icons

The following icons are available by default:

| Key | Default Icon | Usage |
|-----|--------------|-------|
| `back` | ArrowLeftIcon | Back buttons |
| `create` | PlusIcon | Create/Add buttons |
| `edit` | PencilIcon | Edit buttons |
| `delete` | TrashIcon | Delete buttons |
| `view` | EyeIcon | View buttons |
| `success` | CheckCircleIcon | Success toasts |
| `error` | ExclamationCircleIcon | Error toasts |
| `info` | InformationCircleIcon | Info toasts |
| `warning` | ExclamationTriangleIcon | Warning toasts |
| `settings` | CogIcon | Settings |
| `document` | DocumentIcon | Documents |

### Override Icons via uiConfig

```typescript
import { Application } from '@hero-dynamic-form/client';
import { ArrowUturnLeftIcon, StarIcon } from '@heroicons/react/24/solid';

<Application
  schemas={schemas}
  clientConfig={config}
  uiConfig={{
    icons: {
      back: ArrowUturnLeftIcon,     // Override default back icon
      create: StarIcon,              // Override default create icon
      myCustomAction: MyCustomIcon,  // Add new custom icon
    }
  }}
/>
```

### Schema-Level Icons

Schemas can define their own icons (e.g., for custom actions):

```typescript
const userSchema = {
  resourceName: 'users',
  name: 'User',
  // Schema-level icon map
  icons: {
    approve: ApproveIcon,
    reject: RejectIcon,
  },
  actions: [
    {
      name: 'approve',
      type: 'record',
      method: 'POST',
      icon: ApproveIcon,  // Action-specific icon
    }
  ]
};
```

### Using Icons in Components

```typescript
import { useUI } from '@hero-dynamic-form/client';

function MyComponent() {
  const { getIcon, hasIcon } = useUI();

  // Get icon with fallback
  const BackIcon = getIcon('back');
  const CustomIcon = getIcon('myCustomAction', DefaultFallback);

  // Check if icon exists
  if (hasIcon('approve')) {
    const ApproveIcon = getIcon('approve');
  }

  return (
    <button>
      <BackIcon className="h-4 w-4 mr-1" />
      Back
    </button>
  );
}
```

### Runtime Icon Registration

```typescript
import { useUI } from '@hero-dynamic-form/client';

function MyComponent() {
  const { registerIcons } = useUI();

  useEffect(() => {
    // Register icons at runtime
    registerIcons({
      dynamicIcon: DynamicIcon,
      anotherIcon: AnotherIcon,
    });
  }, []);
}
```

### Exported Types

```typescript
import type {
  UIConfig,
  UIProviderProps,
  IconType,
  IconMap,
  BuiltInIconType,
} from '@hero-dynamic-form/client';
```

## CLI Tool - Quick Start

### Initialize a New Project

One command to create a complete admin panel with auto-generated CRUD API:

```bash
# Create new project folder
mkdir my-admin && cd my-admin

# Initialize with CLI (installs everything!)
npx @hero-dynamic-form/client init
```

That's it! The CLI will:
- Create all project files
- Install all dependencies
- Setup TypeORM + PostgreSQL
- Configure @hero-dynamic-form/core
- Ready to run!

### What Gets Created

```
my-admin/
├── src/
│   ├── backend/
│   │   └── index.ts              # DynamicFormCore configuration
│   ├── datasource/
│   │   ├── index.ts              # TypeORM DataSource
│   │   └── entity/
│   │       └── User.ts           # Example User entity
│   └── frontend/
│       ├── index.html
│       ├── index.tsx
│       └── styles.css
├── webpack.dev.cjs
├── webpack.prod.cjs
├── tailwind.config.js
├── postcss.config.js
├── tsconfig.json
├── .babelrc
├── .env                          # Database config
├── .gitignore
└── package.json
```

### Run Your Project

```bash
# 1. Create database
createdb hero_admin_db

# 2. Update .env with your database credentials

# 3. Start development
npm run dev
```

Open in browser:
- **Frontend**: http://localhost:3000
- **Backend API**: http://localhost:4000
- **Health check**: http://localhost:4000/health
- **Schema**: http://localhost:4000/schema

### Auto-Generated CRUD API

Every entity you add automatically gets a full REST API:

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/user` | List all users |
| GET | `/api/user/:id` | Get user by ID |
| POST | `/api/user` | Create new user |
| PUT | `/api/user/:id` | Update user |
| DELETE | `/api/user/:id` | Delete user |

### Adding More Entities

**1. Create a new entity:**

```typescript
// src/datasource/entity/Product.ts
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity({ name: "products" })
export class Product {
  @PrimaryGeneratedColumn()
  id!: number;

  @Column({ type: "varchar", length: 255 })
  name!: string;

  @Column({ type: "decimal", precision: 10, scale: 2 })
  price!: number;

  @Column({ type: "int", default: 0 })
  stock!: number;
}
```

**2. Add to datasource:**

```typescript
// src/datasource/index.ts
import { Product } from "./entity/Product";

export const datasource = new DataSource({
  // ...
  entities: [User, Product],  // Add here
});
```

**3. Register resource:**

```typescript
// src/backend/index.ts
import { Product } from "../datasource/entity/Product";

const config: DynamicFormCoreConfig = {
  // ...
  resources: [
    { name: "user", entity: User },
    { name: "product", entity: Product },  // Add here
  ],
};
```

**4. Restart server** - New endpoints available:
- `GET /api/product`
- `POST /api/product`
- etc.

### Generated Backend Code

The CLI generates a complete DynamicFormCore setup:

```typescript
// src/backend/index.ts
import "reflect-metadata";
import { datasource } from "../datasource";
import { User } from "../datasource/entity/User";
import { DynamicFormCore, DynamicFormCoreConfig } from "@hero-dynamic-form/core";
import { TypeORMEntityInspector } from "@hero-dynamic-form/typeorm-inspector";

const PORT = Number(process.env.PORT) || 4000;

const main = async () => {
  await datasource.initialize();
  console.log("Database connected");

  const config: DynamicFormCoreConfig = {
    dataSource: datasource as any,
    entityInspector: new TypeORMEntityInspector(),
    resources: [
      { name: "user", entity: User },
    ],
    backend: { port: PORT },
    cors: {
      origin: ["http://localhost:3000"],
      credentials: true,
    },
  };

  const app = new DynamicFormCore(config);
  await app.init();
  app.start();
};

main().catch(console.error);
```

### Installed Dependencies

The CLI installs everything you need:

**Runtime:**
- `@hero-dynamic-form/core` - Core framework
- `@hero-dynamic-form/client` - React components
- `@hero-dynamic-form/typeorm-inspector` - TypeORM adapter
- `typeorm` - ORM
- `pg` - PostgreSQL driver
- `reflect-metadata` - Required for TypeORM
- `react`, `react-dom` - Frontend

**Development:**
- `typescript`, `ts-node` - TypeScript
- `webpack`, `webpack-dev-server` - Frontend bundling
- `tailwindcss`, `postcss` - CSS
- `nodemon` - Auto-restart
- `concurrently` - Run multiple scripts

### npm Scripts

```json
{
  "dev": "concurrently \"npm run dev:frontend\" \"npm run dev:backend\"",
  "dev:frontend": "webpack serve --config webpack.dev.cjs",
  "dev:backend": "dotenv -e .env -- nodemon ...",
  "build": "npm run build:frontend && npm run build:backend",
  "start:backend": "node dist/backend/index.js"
}
```

## Development

```bash
# Install dependencies
pnpm install

# Build the package
pnpm run build

# Watch mode for development
pnpm run dev
```

## Package Exports

This package exports:

```json
{
  ".": "./dist/index.js",           // Components (Button, Card, etc.)
  "./tailwind-preset": "./tailwind.preset.js"  // Tailwind preset
}
```

### Files Included

- `dist/` - Compiled JavaScript (ESM + CJS) with TypeScript definitions
- `src/` - Source code (for debugging & Tailwind scanning)
- `tailwind.preset.js` - **Tailwind preset** (recommended for consumers)
- `postcss.config.mjs` - PostCSS configuration
- `bin/runner.cjs` - CLI tool for project initialization

## Requirements

- React >= 18.0.0
- Consumer project must have Tailwind CSS configured

## License

MIT