npm package: @qshelter/mortgage-kanban-board
Description: A flexible, customizable React Kanban board component with drag-and-drop, validation, and form integration
Author: Your Name
Latest version: 1.1.2

# Kanban Board React

A flexible, customizable React Kanban board component with drag-and-drop functionality, status validation, and form integration.

## Features

- 🎯 **Drag & Drop**: Intuitive drag-and-drop interface with visual feedback
- 🔒 **Validation**: Built-in and custom validation for status transitions
- 📝 **Forms**: Configurable forms for complex status changes
- 🎨 **Customizable**: Fully customizable cards, headers, and styling
- 🔐 **Permissions**: Role-based access control for actions
- 📱 **Responsive**: Works great on desktop and mobile
- 🎭 **TypeScript**: Full TypeScript support with comprehensive types
- ♿ **Accessible**: Built with accessibility in mind
- 💬 **Tooltips**: Simple hover tooltips for enhanced UX

## Installation

```bash
npm install @your-org/kanban-board-react
# or
yarn add @your-org/kanban-board-react
```

## Quick Start

```tsx
import React from 'react';
import { KanbanBoard, KanbanConfig, KanbanItem } from '@your-org/kanban-board-react';
import '@your-org/kanban-board-react/dist/index.css';

const config: KanbanConfig = {
  columns: [
    { id: 'todo', label: 'To Do', value: 'todo', color: '#f59e0b' },
    { id: 'progress', label: 'In Progress', value: 'in_progress', color: '#3b82f6' },
    { id: 'review', label: 'Review', value: 'review', color: '#8b5cf6' },
    { id: 'done', label: 'Done', value: 'done', color: '#10b981' },
  ],
  transitions: [
    { from: 'todo', to: 'in_progress', isValid: true },
    { from: 'in_progress', to: 'review', isValid: true },
    { from: 'review', to: 'done', isValid: true, requiresConfirmation: true },
    { from: 'review', to: 'in_progress', isValid: true }, // Back to progress
  ],
  dragAndDrop: { enabled: true },
};

const items: KanbanItem[] = [
  {
    id: '1',
    title: 'Task 1',
    description: 'Complete the user authentication',
    status: 'todo',
  },
  {
    id: '2',
    title: 'Task 2',
    description: 'Design the dashboard layout',
    status: 'in_progress',
  },
];

function MyKanbanBoard() {
  const handleItemMove = async (item: KanbanItem, fromStatus: string, toStatus: string) => {
    // Update your backend/state
    console.log(\`Moving \${item.title} from \${fromStatus} to \${toStatus}\`);
  };

  return (
    <KanbanBoard
      items={items}
      config={config}
      actions={{ onItemMove: handleItemMove }}
    />
  );
}
```

## Advanced Usage - Mortgage Workflow Example

Here's how you can recreate your mortgage workflow:

```tsx
import React from 'react';
import { KanbanBoard, KanbanConfig, KanbanItem } from '@your-org/kanban-board-react';

const mortgageConfig: KanbanConfig = {
  columns: [
    {
      id: 'provisional_offer',
      label: 'Provisional Offer Accepted',
      value: 'provisional_offer_accepted',
      color: '#f97316',
      description: 'Customer has accepted the initial mortgage offer and is ready to proceed',
      showTooltip: true, // Show tooltip on hover with description
    },
    {
      id: 'pre_approval',
      label: 'Pre-Approval',
      value: 'pre_approval',
      color: '#3b82f6',
      description: 'Application is under review for pre-approval',
    },
    {
      id: 'approved',
      label: 'Application Approved',
      value: 'application_approval',
      color: '#10b981',
      description: 'Final mortgage application has been approved',
    },
    {
      id: 'equity_paid',
      label: 'Equity Paid',
      value: 'equity_paid',
      color: '#6366f1',
      description: 'Customer has successfully paid the required equity',
    },
    {
      id: 'docs_sent',
      label: 'Documents Sent to Bank',
      value: 'document_sent_to_bank',
      color: '#8b5cf6',
      description: 'All documents sent to partner bank for review',
    },
    {
      id: 'bank_offer',
      label: 'Offer from Bank',
      value: 'offer_from_bank',
      color: '#ec4899',
      description: 'Bank has generated formal mortgage offer',
    },
    {
      id: 'accepted',
      label: 'Offer Letter Acceptance',
      value: 'offer_letter_acceptance',
      color: '#f59e0b',
      description: 'Customer has accepted bank mortgage offer',
    },
    {
      id: 'disbursement',
      label: 'Disbursement',
      value: 'disbursement',
      color: '#14b8a6',
      description: 'Loan amount is being processed for disbursement',
    },
    {
      id: 'closed',
      label: 'Closed',
      value: 'closed',
      color: '#22c55e',
      description: 'Mortgage process completed successfully',
    },
    {
      id: 'cancelled',
      label: 'Cancelled',
      value: 'cancelled',
      color: '#ef4444',
      description: 'Application cancelled or withdrawn',
    },
  ],
  transitions: [
    { from: 'provisional_offer_accepted', to: 'pre_approval', isValid: true },
    { from: 'pre_approval', to: 'application_approval', isValid: true },
    {
      from: 'application_approval',
      to: 'equity_paid',
      isValid: true,
      requiresForm: true,
      formFields: [
        {
          name: 'equityAmount',
          label: 'Equity Amount',
          type: 'text',
          required: true
        },
        {
          name: 'paymentProof',
          label: 'Payment Proof',
          type: 'file',
          required: true
        },
      ]
    },
    { from: 'equity_paid', to: 'document_sent_to_bank', isValid: true },
    { from: 'document_sent_to_bank', to: 'offer_from_bank', isValid: true },
    { from: 'offer_from_bank', to: 'offer_letter_acceptance', isValid: true },
    { from: 'offer_letter_acceptance', to: 'disbursement', isValid: true },
    {
      from: 'disbursement',
      to: 'closed',
      isValid: true,
      requiresConfirmation: true
    },

    // Cancellation can happen from any status
    ...['provisional_offer_accepted', 'pre_approval', 'application_approval', 'equity_paid', 'document_sent_to_bank', 'offer_from_bank', 'offer_letter_acceptance', 'disbursement'].map(status => ({
      from: status,
      to: 'cancelled',
      isValid: true,
      requiresForm: true,
      formFields: [
        {
          name: 'reason',
          label: 'Cancellation Reason',
          type: 'textarea',
          required: true
        },
      ]
    })),
  ],
  dragAndDrop: {
    enabled: true,
    requireConfirmation: true
  },
  permissions: {
    canDrag: (item) => item.metadata?.userRole === 'admin',
    canDrop: (item, targetStatus) => {
      // Custom business logic
      return item.metadata?.userRole === 'admin';
    }
  }
};

function MortgageKanbanBoard({ mortgages, onStatusUpdate }) {
  const items: KanbanItem[] = mortgages.map(mortgage => ({
    id: mortgage.id,
    title: \`Mortgage #\${mortgage.id}\`,
    description: \`\${mortgage.customer.name} - \${mortgage.property.address}\`,
    status: mortgage.status,
    metadata: {
      mortgage,
      userRole: 'admin', // from your auth context
    }
  }));

  const handleItemMove = async (item: KanbanItem, fromStatus: string, toStatus: string) => {
    await onStatusUpdate(item.metadata.mortgage, toStatus, item.metadata.formData);
  };

  const renderCustomCard = (item: KanbanItem) => (
    <div className="mortgage-card">
      <h4>{item.title}</h4>
      <p>{item.description}</p>
      <div className="mortgage-details">
        <span>Amount: \${item.metadata.mortgage.amount}</span>
        <span>Customer: {item.metadata.mortgage.customer.name}</span>
      </div>
    </div>
  );

  return (
    <KanbanBoard
      items={items}
      config={mortgageConfig}
      actions={{ onItemMove: handleItemMove }}
      renderCustomCard={renderCustomCard}
      className="mortgage-kanban"
    />
  );
}
```

## API Reference

### KanbanBoard Props

| Prop                   | Type                                                   | Required | Description                 |
| ---------------------- | ------------------------------------------------------ | -------- | --------------------------- |
| \`items\`              | \`KanbanItem[]\`                                       | ✅       | Array of items to display   |
| \`config\`             | \`KanbanConfig\`                                       | ✅       | Board configuration         |
| \`actions\`            | \`KanbanActions\`                                      | ❌       | Event handlers              |
| \`className\`          | \`string\`                                             | ❌       | CSS class name              |
| \`loading\`            | \`boolean\`                                            | ❌       | Loading state               |
| \`emptyStateMessage\`  | \`string\`                                             | ❌       | Message for empty columns   |
| \`showEmptyColumns\`   | \`boolean\`                                            | ❌       | Show empty state in columns |
| \`renderCustomCard\`   | \`(item: KanbanItem) => ReactNode\`                    | ❌       | Custom card renderer        |
| \`renderCustomHeader\` | \`(column: KanbanColumn, count: number) => ReactNode\` | ❌       | Custom header renderer      |
| \`onError\`            | \`(error: Error) => void\`                             | ❌       | Error handler               |

### KanbanConfig

```tsx
interface KanbanConfig {
  columns: KanbanColumn[];
  transitions: StatusTransition[];
  dragAndDrop?: {
    enabled: boolean;
    requireConfirmation?: boolean;
    customValidation?: (
      item: KanbanItem,
      targetStatus: string
    ) => Promise<boolean>;
  };
  permissions?: {
    canDrag?: (item: KanbanItem) => boolean;
    canDrop?: (item: KanbanItem, targetColumn: string) => boolean;
    canEdit?: (item: KanbanItem) => boolean;
  };
}
```

## Styling

The component comes with default styles, but you can customize everything:

```css
/* Override default styles */
.kanban-board {
  background: #f8fafc;
}

.kanban-column {
  background: white;
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.kanban-card {
  background: white;
  border: 1px solid #e2e8f0;
  border-radius: 6px;
  padding: 12px;
  margin-bottom: 8px;
}

.kanban-card:hover {
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
```

## License

MIT © [Your Name](https://github.com/your-org)