@qshelter/mortgage-kanban-board
v1.1.2
Published
A flexible, customizable React Kanban board component with drag-and-drop, validation, and form integration
Maintainers
Readme
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
npm install @your-org/kanban-board-react
# or
yarn add @your-org/kanban-board-reactQuick Start
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:
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
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:
/* 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
