hazo_collab_forms
v1.6.15
Published
Collaboration form elements
Downloads
3,497
Readme
Hazo Collab Forms
React form components with integrated chat collaboration, built for Next.js with TypeScript and Tailwind CSS.
Features
- Integrated Chat Collaboration: Each form field supports real-time chat discussions via HazoChat
- Read-Only Summary Views: Display form data in summary format with HazoCollabFormSummary component
- Advanced Field Controls: Visibility toggle, lock/unlock, field duplication, soft delete, and notes
- Notes Integration: Database-backed notes on fields via hazo_notes package (optional)
- Prop-Based Controls: Enable/disable features via
enable_*props for full control - File Upload with Validation: Built-in file upload with custom validation callbacks
- Mandatory File Indicator: Visual indicators (red asterisk) for required file uploads
- Custom File Validators: Implement custom validation logic before file upload
- Form Sets: JSON-based dynamic form generation with full file upload support
- Data OK Workflows: Checkbox and multi-state validation with auto-hide/auto-lock features
- Hidden States: Restrict available data OK states per field or form (e.g., hide "Pending" for agents)
- Type-Safe: Written in TypeScript with comprehensive type definitions
- Tailwind CSS Styling: Fully customizable with Tailwind CSS classes
- shadcn/ui Components: Built on top of accessible Radix UI primitives
Installation
Step 1: Install the Package
npm install hazo_collab_formsStep 2: Install Peer Dependencies
# Core React dependencies (skip if already installed)
npm install react react-dom
# UI dependencies
npm install react-icons sonner lucide-react
npm install @radix-ui/react-dialog @radix-ui/react-label
# Hazo ecosystem packages (required for chat and notes functionality)
npm install hazo_chat hazo_ui hazo_auth hazo_config hazo_notesStep 3: Install shadcn/ui Components
This package requires shadcn/ui components. If you haven't initialized shadcn/ui yet:
npx shadcn@latest initThen install the required components:
# Core components (required for all form fields)
npx shadcn@latest add button label dialog tooltip sonner
# For HazoCollabFormCombo (dropdown/select)
npx shadcn@latest add popover command
# For HazoCollabFormDate (date picker)
npx shadcn@latest add calendar
# For file upload functionality (required when using accept_files prop)
npx shadcn@latest add accordion
# Optional but recommended
npx shadcn@latest add separator cardStep 4: Configure Next.js
Add to your next.config.js:
const nextConfig = {
transpilePackages: ['hazo_collab_forms'],
};
module.exports = nextConfig;Step 5: Configure Tailwind CSS
Add to tailwind.config.ts content array:
content: [
// ... your existing paths
"./node_modules/hazo_collab_forms/**/*.{js,ts,jsx,tsx}",
],Step 6: Create Config Files
Copy the template config files to your project root:
mkdir -p config
cp node_modules/hazo_collab_forms/templates/config/hazo_collab_forms_config.ini ./config/
cp node_modules/hazo_collab_forms/templates/*.ini ./This creates:
config/hazo_collab_forms_config.ini- Main package config (in config/ subdirectory)hazo_chat_config.ini- Chat functionality confighazo_auth_config.ini- Authentication config
Step 7: Verify Installation
npx hazo-collab-forms-verifyThis checks all dependencies, config files, and shadcn components are properly installed.
Quick Reference
One-Line Install (All Dependencies)
npm install hazo_collab_forms react react-dom react-icons sonner lucide-react \
@radix-ui/react-dialog @radix-ui/react-label hazo_chat hazo_ui hazo_auth hazo_config hazo_notesOne-Line shadcn Install (All Components)
npx shadcn@latest add button label dialog tooltip sonner popover command calendar accordion separator cardUsage
Basic Example
'use client';
import { HazoCollabFormInputbox } from 'hazo_collab_forms';
import { useState } from 'react';
export default function MyForm() {
const [value, setValue] = useState('');
return (
<HazoCollabFormInputbox
label="Your Name"
value={value}
onChange={setValue}
field_data_id="user-name"
field_name="User Name"
hazo_chat_receiver_user_id="recipient-user-id"
/>
);
}File Upload with Validation
'use client';
import { HazoCollabFormInputbox } from 'hazo_collab_forms';
import { useState } from 'react';
export default function DocumentUpload() {
const [value, setValue] = useState('');
return (
<HazoCollabFormInputbox
label="Invoice Upload"
value={value}
onChange={setValue}
accept_files={true}
files_dir="public/uploads"
min_files={1} // Shows red asterisk (mandatory indicator)
max_files={5}
file_accept=".pdf"
file_validator={(file) => {
// Custom validation - filename pattern check
if (!file.name.match(/^INV-\d{4}\.pdf$/i)) {
return 'File name must match pattern: INV-0000.pdf';
}
// Size check (5MB limit)
if (file.size > 5 * 1024 * 1024) {
return 'File size must be under 5MB';
}
return null; // Accept file
}}
/>
);
}Advanced Field Control Icons
The package exports standalone control icons for building custom form workflows:
'use client';
import {
HazoCollabFormInputbox,
CollabFormVisibilityIcon,
CollabFormLockIcon,
CollabFormAddEntryIcon,
CollabFormDeleteEntryIcon,
CollabFormDeleteFieldIcon,
} from 'hazo_collab_forms';
import { useState } from 'react';
export default function AdvancedForm() {
const [value, setValue] = useState('');
const [visibility, setVisibility] = useState<'visible' | 'hidden'>('visible');
const [locked, setLocked] = useState(false);
return (
<div>
{/* Visibility Toggle */}
<CollabFormVisibilityIcon
label="Company Name"
visibility={visibility}
on_visibility_change={setVisibility}
/>
{/* Lock/Unlock Field */}
<CollabFormLockIcon
label="Company Name"
locked={locked}
on_lock_change={setLocked}
/>
{/* Add Entry - duplicate field */}
<CollabFormAddEntryIcon
label="Contact Person"
field_id="contact_person"
on_add_entry={(field_id) => console.log('Add entry:', field_id)}
/>
{/* Delete Entry - remove duplicated field */}
<CollabFormDeleteEntryIcon
label="Contact Person"
field_id="contact_person_2"
on_delete_entry={(field_id) => console.log('Delete entry:', field_id)}
/>
{/* Delete Field - soft delete */}
<CollabFormDeleteFieldIcon
label="Optional Field"
field_id="optional_field"
on_delete={(field_id) => console.log('Delete field:', field_id)}
/>
</div>
);
}Icon Components:
| Icon Component | Description |
|----------------|-------------|
| CollabFormVisibilityIcon | Toggle field visibility (show/hide) |
| CollabFormLockIcon | Toggle field lock (read-only mode) |
| CollabFormAddEntryIcon | Duplicate a field entry |
| CollabFormDeleteEntryIcon | Remove a duplicated entry |
| CollabFormDeleteFieldIcon | Soft delete a field |
Note: All controls are shown based on enable_* props. The consuming app decides which controls to render by passing the appropriate props.
Field-Level Controls in HazoCollabFormSet
When using HazoCollabFormSet with JSON-based field definitions, you can configure lock, visibility, and delete controls at the field level. This allows different fields to have different controls.
Cascade Priority:
- Field-level
FieldConfigproperties → overrides form-level defaults - Form-level
HazoCollabFormSetProps→ applies to all fields - Hardcoded fallback →
false(disabled)
'use client';
import { HazoCollabFormSet, FieldConfig } from 'hazo_collab_forms';
const fields: FieldConfig[] = [
{
id: "customer_name",
label: "Customer Name",
field_type: "field",
component_type: "HazoCollabFormInputbox",
value: "",
// Uses form-level defaults
},
{
id: "tax_file_number",
label: "Tax File Number",
field_type: "field",
component_type: "HazoCollabFormInputbox",
value: "",
enable_lock: true,
enable_visibility_toggle: true, // Field-level override
enable_delete: false
},
{
id: "internal_notes",
label: "Internal Notes",
field_type: "field",
component_type: "HazoCollabFormTextArea",
value: "",
enable_lock: false, // Hide lock on this field
enable_visibility_toggle: true,
enable_delete: true // Show delete on this field
}
];
export default function CustomerForm() {
return (
<HazoCollabFormSet
fields_set={{ name: "Customer Form", fields }}
enable_lock={true} // Default for all fields
enable_visibility_toggle={false} // Default for all fields
enable_delete={false} // Default for all fields
/>
);
}Available FieldConfig Control Properties:
enable_lock?: boolean- Show lock iconenable_visibility_toggle?: boolean- Show visibility toggle iconenable_delete?: boolean- Show delete iconenable_data_ok?: boolean- Show data OK controlenable_notes?: boolean- Show notes iconenable_chat?: boolean- Show chat iconchat_read_only?: boolean- View-only chat mode (no input, just viewing)locked?: boolean- Initial lock statevisibility?: 'visible' | 'hidden'- Initial visibility state
Group-Level Control Propagation (Inheritance)
Groups can set enable_* props that automatically propagate to all child fields. This simplifies configuration when all fields in a group need the same controls.
Cascade Priority (highest to lowest):
- Field-level
enable_*in FieldConfig - Individual field override - Parent-group
enable_*- Inherited from parent group - Form-level
enable_*in HazoCollabFormSetProps - Applies to all fields - Hardcoded fallback -
false
Inheritable Control Props:
enable_lock- Lock/protect field controlenable_visibility_toggle- Show/hide field controlenable_delete- Delete field controlenable_data_ok- Data OK validation controlenable_notes- Notes controlenable_chat- Chat control (requireschat_group_idat form level)
const fields: FieldConfig[] = [
{
id: "tax_details",
label: "Tax Information",
field_type: "group",
// These propagate to all children
enable_lock: true,
enable_visibility_toggle: true,
enable_notes: true,
enable_chat: true,
sub_fields: [
// Inherits all enable_* from parent
{ id: "tfn", label: "Tax File Number", component_type: "HazoCollabFormInputbox", value: "" },
// Override: explicitly disable lock and chat
{ id: "abn", label: "ABN", component_type: "HazoCollabFormInputbox", value: "", enable_lock: false, enable_chat: false },
]
}
];Utility Function:
import { resolve_field_controls } from 'hazo_collab_forms';
// Resolve effective controls for a field considering inheritance
const resolved = resolve_field_controls(field, parent_group, form_settings);
// Returns: { enable_lock, enable_visibility_toggle, enable_delete, enable_data_ok, enable_notes, enable_chat }Unified Field Metadata Sync
Control field metadata (visibility, locked, deleted) from a parent component without remounting the form. This enables syncing state between form and summary views.
Props:
field_metadata?: Record<string, FieldMetadataInput>- External metadata for controlled syncon_field_metadata_change?: (change: FieldMetadataChange) => void- Unified callback for all metadata changes
Types:
interface FieldMetadataInput {
visibility?: 'visible' | 'hidden'; // or hidden?: boolean
locked?: boolean;
data_ok?: boolean | DataOkState; // or dataOk
deleted?: boolean;
}
interface FieldMetadataChange {
field_id: string;
visibility?: 'visible' | 'hidden';
locked?: boolean;
data_ok?: boolean | DataOkState;
deleted?: boolean;
}Usage Example:
import { HazoCollabFormSet } from 'hazo_collab_forms';
import type { FieldMetadataInput, FieldMetadataChange } from 'hazo_collab_forms';
import { useState } from 'react';
function MyForm({ fields }) {
const [fieldMetadata, setFieldMetadata] = useState<Record<string, FieldMetadataInput>>({
tax_file_number: { visibility: 'hidden', locked: true },
name: { visibility: 'visible', locked: false },
});
// Unified handler - called for any metadata change
const handleMetadataChange = (change: FieldMetadataChange) => {
setFieldMetadata(prev => ({
...prev,
[change.field_id]: { ...prev[change.field_id], ...change }
}));
};
return (
<HazoCollabFormSet
fields_set={fields}
enable_visibility_toggle={true}
enable_lock={true}
field_metadata={fieldMetadata}
on_field_metadata_change={handleMetadataChange}
/>
);
}Benefits:
- No component remount needed for metadata sync
- Parent has full control over metadata state
- Unified callback simplifies state management
- Backward compatible with existing callbacks (
on_visibility_change,on_lock_change, etc.)
Normalization Helper:
import { normalize_field_metadata_input } from 'hazo_collab_forms';
// Normalize input that uses alternative naming conventions
const input = { hidden: true, dataOk: 'ok', locked: true };
const normalized = normalize_field_metadata_input(input);
// Result: { visibility: 'hidden', data_ok: 'ok', locked: true }Read-Only Summary View
Display form data in a read-only summary format for review workflows:
'use client';
import { HazoCollabFormSummary, type FormSection } from 'hazo_collab_forms';
import { LuUser, LuFileText } from 'react-icons/lu';
const iconMap = {
LuUser: LuUser,
LuFileText: LuFileText,
};
const sections: FormSection[] = [
{
group_name: "Personal Details",
icon: "LuUser",
field_list: [
{ id: "name", label: "Full Name", component_type: "HazoCollabFormInputbox", value: "" },
{ id: "email", label: "Email", component_type: "HazoCollabFormInputbox", value: "" },
]
},
{
group_name: "Tax Details",
icon: "LuFileText",
field_list: [
{ id: "tfn", label: "Tax File Number", component_type: "HazoCollabFormInputbox", value: "" },
]
}
];
export default function FormSummaryPage() {
return (
<HazoCollabFormSummary
sections={sections}
form_data={{
name: "John Doe",
email: "[email protected]",
tfn: "123456789"
}}
render_icon={(iconName) => {
const Icon = iconMap[iconName];
return Icon ? <Icon className="h-5 w-5" /> : null;
}}
show_edit_buttons={true}
on_edit_section={(index) => router.push(`/edit/${index}`)}
/>
);
}With Full Controls:
<HazoCollabFormSummary
sections={sections}
form_data={formData}
field_metadata={fieldMetadata}
// Enable field controls - each enabled via props
// Use *_editable props to make controls clickable
enable_data_ok={true}
data_ok_mode="multi_state"
data_ok_editable={true}
enable_notes={true}
enable_chat={true}
chat_group_id="review-session-123"
chat_read_only={false} // Set to true for view-only chat mode
enable_visibility_toggle={true}
visibility_editable={true}
enable_lock={true}
lock_editable={true}
enable_delete={true}
// Display settings
controls_display="popover"
show_edit_buttons={true}
on_edit_section={(index) => handleEdit(index)}
// Callbacks
on_data_ok_change={(fieldId, value) => console.log('Data OK changed:', fieldId, value)}
on_notes_change={(fieldId, notes) => console.log('Notes updated:', fieldId, notes)}
on_visibility_change={(fieldId, visibility) => console.log('Visibility changed:', fieldId, visibility)}
on_lock_change={(fieldId, locked) => console.log('Lock changed:', fieldId, locked)}
// Auto-actions
on_data_ok_hidden={true} // Auto-hide when marked OK
on_data_ok_protected={true} // Auto-lock when marked OK
/>Key Features:
- Composable design - enable only the features you need
- Section icons with custom rendering
- Edit navigation buttons
- Field controls: data_ok, notes, chat, lock, visibility, delete
- Two display modes: inline or popover
- Row-level controls on DataTables
- Auto-hide/auto-lock on data_ok change
- Chat read-only mode for view-only scenarios
Chat Read-Only Mode
Enable view-only chat mode where users can see chat history but cannot send new messages. Useful for archived forms, review-only views, or user permission scenarios.
Props:
chat_read_only?: boolean- Form-level setting (applies to all fields)- Field-level
chat_read_onlyin FieldConfig overrides form-level setting
// HazoCollabFormSet - all fields have view-only chat
<HazoCollabFormSet
fields_set={fields}
chat_group_id={groupId}
chat_read_only={true}
/>
// HazoCollabFormSummary - view-only chat
<HazoCollabFormSummary
sections={sections}
form_data={formData}
enable_chat={true}
chat_group_id={groupId}
chat_read_only={true}
/>
// Field-level override in FieldConfig
const fields: FieldConfig[] = [
{
id: "active_discussion",
label: "Active Discussion",
component_type: "HazoCollabFormInputbox",
chat_read_only: false // Can chat on this field
},
{
id: "archived_data",
label: "Archived Data",
component_type: "HazoCollabFormInputbox",
chat_read_only: true // View-only chat
}
];Components
| Component | Description | Required shadcn |
|-----------|-------------|-----------------|
| HazoCollabFormInputbox | Text input with validation | button, label, dialog |
| HazoCollabFormTextArea | Multi-line text input | button, label, dialog |
| HazoCollabFormCheckbox | Boolean toggle | button, label, dialog |
| HazoCollabFormCombo | Dropdown select with search | + popover, command |
| HazoCollabFormRadio | Radio button group | button, label, dialog |
| HazoCollabFormDate | Date or date-range picker | + calendar |
| HazoCollabFormGroup | Field grouping container | button, label, dialog |
| HazoCollabFormDataTable | Editable data table with aggregations | button, label, dialog |
| HazoCollabFormSet | Complete form with field arrays | all components |
| HazoCollabFormSummary | Read-only summary view | button, label, popover |
| File Upload | When using accept_files prop | + accordion (required) |
Import Paths
// Default: All client-safe components and utilities
import { HazoCollabFormInputbox, cn } from 'hazo_collab_forms';
// Components only
import { HazoCollabFormInputbox } from 'hazo_collab_forms/components';
// Utilities only
import { cn, use_collab_chat } from 'hazo_collab_forms/utils';
// Server-only (config functions)
import { get_config } from 'hazo_collab_forms/lib';Troubleshooting
lucide-react Version Conflicts
Different Hazo packages require different versions of lucide-react. Add this to your package.json:
"overrides": {
"lucide-react": "^0.553.0"
}Missing shadcn Components
If you see errors about missing components, install the specific shadcn component:
npx shadcn@latest add [component-name]Important: If you're using file upload functionality (accept_files prop on any form field), you must install the accordion component:
npx shadcn@latest add accordionVerify Your Setup
Run the verification tool to check for common issues:
npx hazo-collab-forms-verifySee SETUP_CHECKLIST.md for detailed troubleshooting.
Development
Package Structure
- Root: ES module npm package
- test-app: Next.js application for testing
Commands
npm run build # Build the package
npm run dev:package # Watch mode for development
npm run dev:test-app # Build and run test app
npm run build:test-app # Build for production
npm run clean # Remove dist directoryTypeScript Configuration
tsconfig.json: Development (bundler module resolution)tsconfig.build.json: Build (Node16 for ES module output)
ES Module Exports
All exports use explicit .js extensions as required for ES modules:
export * from './lib/index.js';
export * from './components/index.js';License
MIT
