Package Exports
- hazo_collab_forms
- hazo_collab_forms/components
- hazo_collab_forms/lib
- hazo_collab_forms/utils
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 with customizable display modes (embedded, side panel, overlay) and UI controls
- Unified Form View: Multi-mode form display (Edit, Summary, Print, Approval) with shared context
- Read-Only Summary Views: Display form data in summary format with status badges for locked/hidden fields
- Reference Data Display: Show comparison values (e.g., prior year data) below form fields with automatic formatting for currency, booleans, and custom styling
- 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)
- Field Tooltips: Help tooltips with hover cards for field labels and data table columns
- Field Library: Database-backed library for creating, organizing, and reusing form field/group definitions (HazoFieldLibrary)
- Template Generator: Visual drag-and-drop builder for creating form configurations (HazoTemplateGenerator - deprecated, use HazoFieldLibrary)
- Editor Theme: Customizable theming system for template generator and field library UI
- Dynamic Field Management: Add, edit, and delete fields at runtime with dialog components
- Field Selector: Search and select fields from templates with HazoFieldSelectorDialog
- Role-Based Icon Controls: Configure control visibility and behavior per role with icons_behaviour system
- Unified Field Controls: Consistent icon controls across form and summary views with automatic kebab consolidation
- Prop-Based Controls: Enable/disable features via
enable_*props for full control - File Upload with Validation: Built-in file upload with custom validation callbacks
- Document Fields: File-only fields with HazoCollabFormDoc component
- 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
- Autofill Dropzone: LLM-powered document extraction with multi-file support, progressive data table population, and overwrite conflict detection
- Data Table in Summary Views: Proper HTML table rendering for data table fields in Summary, Print, and Approval views with aggregation 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 @radix-ui/react-popover
# 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_public / accept_files_private props)
npx shadcn@latest add accordion
# For HazoTemplateGenerator (visual form builder)
npx shadcn@latest add tabs resizable
# For field tooltips (optional - falls back to native title if not installed)
npx shadcn@latest add hover-card
# 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
For Tailwind CSS v3:
Add to tailwind.config.ts content array:
content: [
// ... your existing paths
"./node_modules/hazo_collab_forms/**/*.{js,ts,jsx,tsx}",
],For Tailwind CSS v4:
Add the @source directive to your globals.css (or main CSS file) after the tailwindcss import:
@import "tailwindcss";
/* Required: Enable Tailwind to scan this package's classes */
@source "../node_modules/hazo_collab_forms/dist";This is required because Tailwind v4's JIT compiler does not scan node_modules/ by default. Without this directive, hover states, colors, and layout utilities from this package will not have CSS generated, resulting in broken styling (transparent backgrounds, missing colors, etc.).
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 @radix-ui/react-popover 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 tabs resizable hover-card separator card sheet scroll-area checkbox selectUsage
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 hazo_files Integration
The file upload system uses hazo_files for centralized file management:
'use client';
import { HazoCollabFormView, FileManagerConfig, FileManagerCallbacks, FileUploadOptions, FormFileAttachment } from 'hazo_collab_forms';
import { useState } from 'react';
export default function DocumentUpload() {
// Define file management callbacks
const fileCallbacks: FileManagerCallbacks = {
upload: async (file: File, options: FileUploadOptions) => {
const formData = new FormData();
formData.append('file', file);
formData.append('field_id', options.field_id);
formData.append('visibility', options.visibility);
const response = await fetch('/api/files/upload', {
method: 'POST',
body: formData,
});
const data = await response.json();
return {
file_id: data.file_id,
ref_id: options.field_id,
file_name: file.name,
file_size: file.size,
mime_type: file.type,
visibility: options.visibility,
attached_at: new Date(),
};
},
remove: async (file_id: string, field_id: string) => {
await fetch(`/api/files/${file_id}`, { method: 'DELETE' });
},
get_download_url: (file_id: string, field_id: string) => {
return `/api/files/${file_id}/download`;
},
// Optional: File status polling
get_status: async (file_ids: string[]) => {
const response = await fetch(`/api/files/status?ids=${file_ids.join(',')}`);
const data = await response.json();
return data.statuses; // Record<file_id, FileStatus>
},
// Optional: Reupload functionality
reupload: async (file_id: string, file: File, options: FileUploadOptions) => {
// Similar to upload, but replaces existing file
const formData = new FormData();
formData.append('file', file);
formData.append('file_id', file_id);
const response = await fetch(`/api/files/${file_id}/reupload`, {
method: 'PUT',
body: formData,
});
const data = await response.json();
return {
file_id: data.file_id,
ref_id: options.field_id,
file_name: file.name,
file_size: file.size,
mime_type: file.type,
visibility: options.visibility,
attached_at: new Date(),
};
},
};
const fileManager: FileManagerConfig = {
callbacks: fileCallbacks,
show_file_status: true, // Show status badges (active, orphaned, etc.)
allow_reupload: true, // Enable reupload button
status_poll_interval: 10000, // Poll status every 10s
};
return (
<HazoCollabFormView
mode="edit"
sections={sections}
form_data={formData}
file_manager={fileManager} // Required for file upload UI to appear
/>
);
}File Manager Configuration
File upload now uses a callback-based system via FileManagerConfig. You must implement the following callbacks:
Required Callbacks:
| Callback | Description |
|---|---|
upload |
Upload a file and return FormFileAttachment |
remove |
Delete a file by file_id |
get_download_url |
Generate download URL for a file |
Optional Callbacks:
| Callback | Description |
|---|---|
get_status |
Poll file status (active, orphaned, soft_deleted, missing) |
reupload |
Replace an existing file with a new version |
Configuration Options:
| Option | Type | Default | Description |
|---|---|---|---|
callbacks |
FileManagerCallbacks | required | File operation callbacks |
show_file_status |
boolean | false | Show status badges on files |
allow_reupload |
boolean | false | Enable reupload button |
status_poll_interval |
number | undefined | Status polling interval (ms) |
Important: File upload UI will only render when file_manager prop is provided to HazoCollabFormView.
File Persistence (Multi-User Scenarios)
When using file uploads in HazoCollabFormView, files uploaded by one user must be persisted to your backend so other users can see them. The component provides callbacks for this purpose.
How File Storage Works:
- Files are stored in
form_datausing the key pattern__files_${field_id} - Private files use the pattern
__private_files_${field_id} - When a user uploads a file,
on_field_files_changeis called with the file metadata - You must save this metadata to your database
- When loading the form for another user, include the file metadata in
form_data
Implementation Example:
'use client';
import { HazoCollabFormView, FileData } from 'hazo_collab_forms';
import { useState, useEffect } from 'react';
interface FormData {
[key: string]: unknown;
}
export default function CollaborativeForm({ formId }: { formId: string }) {
const [formData, setFormData] = useState<FormData>({});
// Load form data including files from your backend
useEffect(() => {
async function loadForm() {
const response = await fetch(`/api/forms/${formId}`);
const data = await response.json();
// data should include __files_* keys with FileData arrays
// Example: { field_name: "value", __files_document_field: [{ file_id: "...", file_name: "..." }] }
setFormData(data);
}
loadForm();
}, [formId]);
// Handle file changes - save to your backend
const handleFilesChange = async (field_id: string, files: FileData[]) => {
// Update local state
setFormData(prev => ({
...prev,
[`__files_${field_id}`]: files
}));
// Persist to your backend
await fetch(`/api/forms/${formId}/files`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ field_id, files })
});
};
// Handle private file changes (if using file visibility feature)
const handlePrivateFilesChange = async (field_id: string, files: FileData[]) => {
setFormData(prev => ({
...prev,
[`__private_files_${field_id}`]: files
}));
await fetch(`/api/forms/${formId}/private-files`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ field_id, files })
});
};
return (
<HazoCollabFormView
mode="edit"
sections={sections}
form_data={formData}
on_form_data_change={setFormData}
on_field_files_change={handleFilesChange}
on_field_private_files_change={handlePrivateFilesChange}
/>
);
}File Attachment Interfaces:
// New callback-based attachment format
interface FormFileAttachment {
file_id: string; // Unique identifier
ref_id: string; // Field ID reference
file_name: string; // Display name
file_size: number; // Size in bytes
mime_type: string; // MIME type (e.g., 'application/pdf')
visibility: 'public' | 'private';
attached_at: Date; // Attachment timestamp
}
// File status for status badges
type FileStatus = 'active' | 'orphaned' | 'soft_deleted' | 'missing';
// Legacy format (still supported for backward compatibility)
interface FileData {
file_path: string; // Server path for download
file_name: string; // Display name
file_size: number; // Size in bytes
file_type: string; // MIME type
file_id: string; // Unique identifier
uploaded_at: Date; // Upload timestamp
visibility?: 'public' | 'private';
}Conversion Utilities:
import {
attachment_to_file_data,
file_data_to_attachment,
is_form_file_attachment
} from 'hazo_collab_forms';
// Convert new format to legacy
const legacy = attachment_to_file_data(attachment);
// Convert legacy to new format
const attachment = file_data_to_attachment(fileData, 'field_id');
// Type guard
if (is_form_file_attachment(item)) {
// item is FormFileAttachment
}Backend API Example (Next.js):
// app/api/forms/[formId]/files/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function POST(
request: NextRequest,
{ params }: { params: { formId: string } }
) {
const { field_id, files } = await request.json();
// Save to your database
// Example with Prisma:
// await prisma.formData.upsert({
// where: { formId_fieldId: { formId: params.formId, fieldId: `__files_${field_id}` } },
// update: { value: JSON.stringify(files) },
// create: { formId: params.formId, fieldId: `__files_${field_id}`, value: JSON.stringify(files) }
// });
return NextResponse.json({ success: true });
}Key Points:
- Files are NOT automatically persisted - you must implement
on_field_files_change - The
form_dataobject must include__files_*keys when loading for other users - Summary mode will only display files if they exist in
form_data - The actual file binary is uploaded separately (handled by the component's upload mechanism)
- Only the file metadata (FileData) needs to be persisted in your form data store
Private File Access Control
Private file sections are only visible to users whose role has control_private_files: { visible: true } in the icons_behaviour controls configuration. This is the recommended way to control private file visibility at the form level.
Role-Based Access (Recommended):
// icons_behaviour.json
{
"roles": [
{
"role_id": "tax_agent",
"controls": {
"control_private_files": { "visible": true, "enabled": true }, // Can see private files
// ... other controls
}
},
{
"role_id": "client",
"controls": {
"control_private_files": { "visible": false, "enabled": false }, // Cannot see private files
// ... other controls
}
}
]
}
// In your component
<HazoCollabFormView
mode="edit"
sections={sections}
form_data={formData}
icons_behaviour={iconsBehaviour}
active_role="tax_agent" // Will have access to private files
/>Priority (highest to lowest):
- Form-level
can_access_private_filesprop (explicit override) - Role-based
control_private_files.visiblefrom icons_behaviour controls - Field-level
can_access_private_filesprop in FieldConfig - hazo_auth API permission check (using
private_files_permission)
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 HazoCollabFormView
When using HazoCollabFormView 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 props → applies to all fields
- Hardcoded fallback →
false(disabled)
'use client';
import { HazoCollabFormView, type 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 (
<HazoCollabFormView
mode="edit"
sections={[{ section_name: "Customer Form", groups: fields }]}
form_data={{}}
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 iconnotes_panel_style?: 'popover' | 'slide_panel'- Notes panel style (default: 'popover')notes_background_color?: string- Notes panel background (Tailwind class)notes_save_mode?: 'explicit' | 'auto'- Notes save behavior (default: 'explicit')enable_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 HazoCollabFormViewProps - 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 { HazoCollabFormView } from 'hazo_collab_forms';
import type { FieldMetadataInput, FieldMetadataChange } from 'hazo_collab_forms';
import { useState } from 'react';
function MyForm({ sections }) {
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 (
<HazoCollabFormView
mode="edit"
sections={sections}
form_data={{}}
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 using HazoCollabFormView with mode="summary":
'use client';
import { HazoCollabFormView, type ViewSection } from 'hazo_collab_forms';
import { LuUser, LuFileText } from 'react-icons/lu';
const render_icon = (iconName: string) => {
const iconMap = { LuUser, LuFileText };
const Icon = iconMap[iconName];
return Icon ? <Icon className="h-5 w-5" /> : null;
};
export default function FormSummaryPage() {
return (
<HazoCollabFormView
mode="summary"
sections={sections}
form_data={{
name: "John Doe",
email: "john@example.com",
tfn: "123456789"
}}
render_icon={render_icon}
show_edit_buttons={true}
on_edit_section={(index) => router.push(`/edit/${index}`)}
/>
);
}With Full Controls:
<HazoCollabFormView
mode="summary"
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="inline"
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 Realtime Mode
Control how chat status is polled for all fields in the form. Useful for reducing network requests and log verbosity.
Props:
chat_realtime_mode?: 'polling' | 'manual'- Form-level setting'polling'(default): Automatically poll for chat status every 5 seconds'manual': Only fetch chat status once on mount and whenrefresh()is called
// Disable automatic polling - only fetch once on mount
<HazoCollabFormView
mode="edit"
sections={sections}
form_data={formData}
enable_chat={true}
chat_group_id="form-session-123"
chat_realtime_mode="manual" // No automatic polling
/>use_field_chat_status Hook:
For custom implementations, use the use_field_chat_status hook directly:
import { use_field_chat_status } from 'hazo_collab_forms';
const { chat_status, refresh, is_loading } = use_field_chat_status({
field_ids: ['field1', 'field2'],
chat_group_id: 'group-id',
enabled: true,
realtime_mode: 'manual', // or 'polling' (default)
poll_interval: 5000, // only used when realtime_mode is 'polling'
});
// chat_status: Record<string, 'none' | 'has_messages' | 'has_unread'>
// refresh(): manually trigger a status refreshChat Customization
Chat Display and Behavior:
The package provides several props to customize chat appearance and behavior:
Props:
chat_read_only?: boolean- View-only mode (users can see chat but cannot send messages)chat_hide_references?: boolean- Hide the references section in chatchat_hide_sidebar?: boolean- Hide the entire document viewer sidebar (requires hazo_chat v5.2.0+)chat_hide_preview?: boolean- Hide message preview/attachment previewchat_display_mode?: 'embedded' | 'side_panel' | 'overlay'- Controls chat rendering mode (default: 'embedded')chat_container_element?: HTMLElement | null- Container for portal rendering with certain display modeschat_log_polling?: boolean- Enable polling console logs for debugging (default: false)
// HazoCollabFormView - customized chat experience
<HazoCollabFormView
mode="edit"
sections={sections}
form_data={formData}
enable_chat={true}
chat_group_id={groupId}
chat_read_only={false}
chat_hide_references={true} // Hide references section
chat_hide_sidebar={false} // Show sidebar (requires hazo_chat v5.2.0+)
chat_hide_preview={false} // Show message previews
chat_display_mode="side_panel" // Render as side panel
chat_log_polling={false} // Disable polling logs
/>
// HazoCollabFormView - summary with view-only chat
<HazoCollabFormView
mode="summary"
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
}
];Field Tooltips
Add help tooltips to field labels and data table columns using the tooltip prop. Tooltips display on hover using shadcn's HoverCard component, with automatic fallback to native title attribute if HoverCard is not available.
Usage in Form Fields:
import { HazoCollabFormInputbox } from 'hazo_collab_forms';
<HazoCollabFormInputbox
label="Tax File Number"
tooltip="Your 9-digit tax identification number (TFN)"
value={tfn}
onChange={setTfn}
/>
// Or with full TooltipConfig
<HazoCollabFormInputbox
label="ABN"
tooltip={{
enabled: true,
content: "Australian Business Number - 11 digits",
position: "right" // optional
}}
value={abn}
onChange={setAbn}
/>Usage in Data Tables:
const columns: DataTableColumn[] = [
{
id: "amount",
label: "Amount",
type: "number",
tooltip: "Enter the invoice amount in AUD",
},
{
id: "date",
label: "Invoice Date",
type: "date",
tooltip: {
enabled: true,
content: "The date shown on the invoice document"
}
}
];Tooltip in FieldConfig:
const fields: FieldConfig[] = [
{
id: "gst_amount",
label: "GST Amount",
component_type: "HazoCollabFormInputbox",
tooltip: "10% of the invoice amount (automatically calculated if empty)",
value: ""
}
];Optional Dependency:
- Tooltips require
hover-cardshadcn component for best experience - Automatically falls back to native HTML title attribute if hover-card is not installed
- Install with:
npx shadcn@latest add hover-card
Field Library (New in v1.9.0)
Database-backed library for creating, organizing, and reusing form field and group definitions. Elements are stored in the hazo_collab_form_elts table and can be organized by area.
Basic Usage:
'use client';
import { HazoFieldLibrary } from 'hazo_collab_forms';
export default function FieldLibraryPage() {
return (
<div className="h-screen">
<HazoFieldLibrary
api_base_url="/api/hazo_connect/sqlite/data"
table_name="hazo_collab_form_elts"
scope_id="my-project"
/>
</div>
);
}Features:
- Fields & Groups tabs: Manage individual fields and group definitions
- Area organization: Categorize elements by area with combobox search and inline creation
- Property editors: Full field configuration using shared tab editors (Basic, Type, Validation, Controls)
- Import/Export: JSON import/export for version control and sharing
- Reference Picker: Link group elements to field library entries
- Library Search: Search and add fields from the library via HazoAddFieldDialog integration
Props:
| Prop | Type | Description |
|---|---|---|
api_base_url |
string | Base URL for the hazo_connect REST API |
table_name |
string | Database table name (default: hazo_collab_form_elts) |
scope_id |
string | null | Scope ID for filtering elements |
className |
string | Additional CSS class |
theme |
Partial<EditorTheme> | Editor theme overrides |
Database Table:
CREATE TABLE hazo_collab_form_elts (
id TEXT PRIMARY KEY,
scope_id TEXT,
area TEXT NOT NULL,
key TEXT NOT NULL,
element TEXT NOT NULL, -- JSON string of FieldConfig
created_at TEXT NOT NULL,
changed_at TEXT NOT NULL
);Editor Theme
Customize the appearance of HazoFieldLibrary and HazoTemplateGenerator with the editor theme system.
import { HazoFieldLibrary, type EditorTheme } from 'hazo_collab_forms';
const customTheme: Partial<EditorTheme> = {
label_style: 'uppercase', // 'uppercase' | 'normal'
tab_style: 'pill', // 'pill' | 'underline'
header_style: 'gradient', // 'gradient' | 'accent' | 'simple'
label_icons: true, // Show icons alongside labels
max_field_width: 'lg', // 'full' | 'lg' | 'md'
};
<HazoFieldLibrary theme={customTheme} ... />
<HazoTemplateGenerator theme={customTheme} ... />Theme Context:
Use EditorThemeProvider and useEditorTheme() for custom components:
import { EditorThemeProvider, useEditorTheme } from 'hazo_collab_forms';
function CustomEditor() {
const theme = useEditorTheme();
// Access theme.label_style, theme.tab_style, etc.
}Autofill Dropzone (LLM Document Extraction)
Drop source documents onto a form group to automatically extract and populate field values using an LLM. Supports multiple files processed sequentially with progressive data application.
Basic Usage:
<HazoCollabFormView
mode="edit"
sections={[{
groups: [{
id: 'income_group',
field_type: 'group',
label: 'Income Details',
autofill_dropzone: true, // Enable the dropzone
autofill_api_endpoint: '/api/autofill',
autofill_prompt_area: 'tax', // Optional: prompt lookup area
autofill_prompt_key: 'income', // Optional: prompt lookup key
fields: [...]
}]
}]}
file_manager={fileManagerConfig} // Required for file upload
form_data={formData}
on_change={handleChange}
/>Multi-file behavior:
- Multiple files can be dropped or selected at once
- Files are processed sequentially through the LLM API
- Array values (data table rows) are appended to existing data, not overwritten
- Scalar values trigger an overwrite confirmation dialog (only on the first conflicting file)
- Progress indicator shows "Processing file N of M..."
AutofillRequest sent to API:
| Field | Type | Description |
|---|---|---|
file_id |
string | Uploaded file ID |
file_name |
string | Original file name |
mime_type |
string | File MIME type |
download_url |
string | URL to download the file |
file_b64 |
string? | Base64-encoded file content (avoids re-download) |
group_id |
string | Group identifier |
prompt_area |
string? | Prompt area for LLM context |
prompt_key |
string? | Prompt key for LLM context |
fields |
AutofillFieldSchema[] | Field schemas for extraction |
AutofillResponse from API:
{
success: boolean;
data: Record<string, unknown>; // Keyed by field_id
error?: string; // Error message if failed
message?: string; // Info message (e.g., "No matching data found")
}Template Generator (Deprecated)
Deprecated: Use
HazoFieldLibraryfor managing form field definitions.HazoTemplateGeneratoris still available but will be removed in a future major version.
Visual form builder for creating form configurations with drag-and-drop interface. Build complex form structures without writing JSON manually.
Basic Usage:
'use client';
import { HazoTemplateGenerator } from 'hazo_collab_forms';
import { useState } from 'react';
export default function TemplateBuilder() {
const [template, setTemplate] = useState({
sections: [],
icons_behaviour: {}
});
return (
<div className="h-screen">
<HazoTemplateGenerator
initial_template={template}
on_template_change={setTemplate}
on_save={(template) => {
console.log('Saved template:', template);
// Save to database or file
}}
on_cancel={() => router.push('/templates')}
/>
</div>
);
}Features:
- Visual Tree Editor: Organize sections, groups, and fields hierarchically
- Live Preview: See your form render in real-time as you build
- Property Editors: Configure field properties, validation, and controls
- Data Table Editor: Visual builder for complex data table configurations
- Icons Behaviour Editor: Configure role-based control visibility
- Template Library: Load and save reusable templates
- Import/Export: JSON import/export for version control
- Full-Screen Preview: Test your form in full-screen dialog
- Undo/Redo: Complete history navigation
Props:
| Prop | Type | Description |
|---|---|---|
initial_template |
Section[] | Starting template structure |
on_template_change |
callback | Called when template changes |
on_save |
callback | Save button handler |
on_cancel |
callback | Cancel button handler |
available_templates |
TemplateOption[] | Predefined templates to load |
on_load_template |
callback | Called when template is loaded |
preview_chat_group_id |
string | Chat group ID for live preview |
readonly |
boolean | Disable editing (view-only mode) |
show_import_export |
boolean | Show import/export buttons |
show_controls_tab |
boolean | Show advanced controls configuration |
Template Structure:
The generator produces standard form configuration:
interface TemplateOutput {
sections: Section[];
icons_behaviour?: IconsBehaviour;
}Required shadcn Components:
- resizable (for panel layout)
- tabs (for editor interface)
- hover-card (optional, for tooltips)
Install with: npx shadcn@latest add resizable tabs
Unified Form View (HazoCollabFormView) - Recommended
A unified component that combines edit, summary, print, and approval views with shared context. Switch between view modes without remounting, preserving state and field metadata.
Note:
HazoCollabFormViewis the recommended component. It replaces the olderHazoCollabFormSet(deprecated, internal only since v1.9.0) andHazoCollabFormSummary(fully removed in v1.9.0).
Basic Usage:
'use client';
import { HazoCollabFormView, type ViewMode } from 'hazo_collab_forms';
import { useState } from 'react';
export default function UnifiedFormPage() {
const [viewMode, setViewMode] = useState<ViewMode>('edit');
const [formData, setFormData] = useState<Record<string, unknown>>({});
return (
<div>
{/* View mode switcher */}
<div className="flex gap-2 mb-4">
<button onClick={() => setViewMode('edit')}>Edit</button>
<button onClick={() => setViewMode('summary')}>Summary</button>
<button onClick={() => setViewMode('print')}>Print</button>
<button onClick={() => setViewMode('approval')}>Approval</button>
</div>
<HazoCollabFormView
sections={sections}
view_mode={viewMode}
form_data={formData}
on_form_data_change={setFormData}
icons_behaviour={iconsBehaviour}
active_role="tax_agent"
enable_chat={true}
chat_group_id="form-session-123"
/>
</div>
);
}View Modes:
| Mode | Description |
|---|---|
edit |
Full form editing with all field controls |
summary |
Read-only summary view for review |
print |
Print-optimized layout |
approval |
Review with data_ok approval workflow |
Key Props:
| Prop | Type | Description |
|---|---|---|
sections |
ViewSection[] | Form sections |
view_mode |
ViewMode | Current view mode |
form_data |
Record<string, unknown> | Form values |
on_form_data_change |
callback | Called when form data changes |
field_metadata |
Record<string, FieldMetadata> | Field metadata (visibility, lock, etc.) |
on_field_metadata_change |
callback | Called when metadata changes |
icons_behaviour |
IconsBehaviour | Role-based control configuration |
active_role |
string | Current user's role |
Context Hooks:
Access form state from nested components using hooks:
import {
useFormViewContext,
useViewMode,
useFormData,
useFieldMetadata,
useFieldValue,
useFieldMeta,
useApprovalStats,
} from 'hazo_collab_forms';
function FieldComponent({ fieldId }) {
const viewMode = useViewMode();
const value = useFieldValue(fieldId);
const { data_ok, locked, visibility } = useFieldMeta(fieldId);
const { total, approved, pending } = useApprovalStats();
// Render based on view mode and field state
}Reference Data (Comparison Values)
Display reference or comparison values below form fields, such as prior year tax data or baseline values. Reference data supports automatic formatting for currency, booleans, and custom styling.
Basic Usage:
import { HazoCollabFormView, type ReferenceData } from 'hazo_collab_forms';
const referenceData: ReferenceData = {
salary_income: {
value: 82450.00, // Formatted as $82,450.00
label: "Prior Year (2023)",
background_color: "bg-blue-50" // Optional Tailwind class
},
has_dependents: {
value: true, // Formatted as "Yes"
label: "2023"
},
filing_status: {
value: "married", // String pass-through
label: "Last year"
}
};
<HazoCollabFormView
mode="edit"
sections={sections}
form_data={formData}
reference_data={referenceData}
/>Value Formatting:
| Type | Formatting | Example |
|---|---|---|
| Boolean | "Yes" / "No" | true → "Yes" |
| Number >= 100 | Currency (AUD) | 82450 → "$82,450.00" |
| Number < 100 | Plain | 15 → "15" |
| String | Pass-through | "text" → "text" |
Supported Modes:
- ✅
mode="edit"- Edit view - ✅
mode="summary"- Summary view - ✅
mode="print"- Print view - ✅
mode="approval"- Approval view
Priority:
Field-level props (reference_value, reference_label, reference_tag_background_color) take precedence over form-level reference_data.
Role-Based Icon Controls (icons_behaviour / GlobalSettings)
Configure which control icons are visible and enabled based on user roles. Supports automatic consolidation into kebab (3-dots) menus and extraction of active controls.
Note: The
IconsBehaviourtype has a new aliasGlobalSettingsthat better represents its expanded scope, which now includes role-based permissions (likecontrol_private_files) in addition to icon behaviors.
JSON Configuration Options:
You can configure icons_behaviour in two ways:
- Embedded (original): Include icons_behaviour directly in the form JSON file
- Separate file (recommended): Store icons_behaviour in a dedicated JSON file for reuse across forms
Separate File Approach (v1.7.0+):
public/data/form-sets/
tax_form_individual_full.json # Sections only
icon_behaviour.json # Icons behaviour config (reusable)Load both files and pass icons_behaviour as a prop:
// Load form configuration
const sectionsResponse = await fetch('/data/form-sets/tax_form_individual_full.json');
const { sections } = await sectionsResponse.json();
const behaviourResponse = await fetch('/data/form-sets/icon_behaviour.json');
const icons_behaviour = await behaviourResponse.json();
// Pass to HazoCollabFormView (edit mode)
<HazoCollabFormView
mode="edit"
sections={sections}
form_data={formData}
icons_behaviour={icons_behaviour}
active_role="tax_agent"
/>
// Pass to HazoCollabFormView (summary mode)
<HazoCollabFormView
mode="summary"
sections={sections}
form_data={formData}
icons_behaviour={icons_behaviour}
active_role="tax_agent"
/>Prop Priority:
icons_behaviourprop (if provided)section.icons_behaviourorsections[0]?.icons_behaviour(backward compatible)
This separation allows:
- Reusing the same icon behavior across multiple forms
- Changing roles without modifying form structure
- Easier maintenance of large form configurations
Configuration Structure:
const icons_behaviour: IconsBehaviour = {
// Data OK display mode
data_ok_mode: 'multistate', // or 'checkbox'
// Controls that have borders
border_on: ['control_data_ok'],
// Display order (left to right)
icon_order: [
'control_data_ok',
'control_skip',
'control_notes',
'control_chat',
'control_visibility',
'control_lock',
'control_delete',
'control_edit_field',
'control_edit_group',
'control_delete_field',
],
// Always shown individually (never in kebab)
always_extracted: ['control_data_ok'],
// Extracted when they have activity
extracted_on_value: ['control_notes', 'control_chat'],
// Role definitions
roles: [
{
role_id: 'tax_agent',
role_name: 'Tax Agent',
role_description: 'Full access to all controls',
controls: {
control_data_ok: { visible: true, enabled: true },
control_notes: { visible: true, enabled: true },
control_chat: { visible: true, enabled: true },
control_visibility: { visible: true, enabled: true },
control_lock: { visible: true, enabled: true },
control_delete: { visible: true, enabled: false },
control_edit_field: { visible: true, enabled: true },
control_edit_group: { visible: true, enabled: true },
control_delete_field: { visible: true, enabled: true },
control_private_files: { visible: true, enabled: true } // Can see private file sections
}
},
{
role_id: 'client',
role_name: 'Client',
role_description: 'Limited to chat and notes',
controls: {
control_chat: { visible: true, enabled: true },
control_notes: { visible: true, enabled: true },
control_data_ok: { visible: true, enabled: false },
control_visibility: { visible: false, enabled: false },
control_lock: { visible: false, enabled: false },
control_delete: { visible: false, enabled: false },
control_edit_field: { visible: false, enabled: false },
control_edit_group: { visible: false, enabled: false },
control_delete_field: { visible: false, enabled: false },
control_private_files: { visible: false, enabled: false } // Cannot see private file sections
}
}
]
};Usage in HazoCollabFormView:
// Edit mode
<HazoCollabFormView
mode="edit"
sections={sections}
form_data={formData}
icons_behaviour={icons_behaviour}
active_role="tax_agent"
/>
// Summary mode
<HazoCollabFormView
mode="summary"
sections={sections}
form_data={formData}
icons_behaviour={icons_behaviour}
active_role="client"
/>Consolidation Rules (Automatic):
When 2+ non-extracted controls are visible, they consolidate into a kebab menu:
- Data OK, Skip, Edit Field, Edit Group: Always inline (hardcoded, never in kebab)
- Controls in always_extracted: Always inline
- Controls in extracted_on_value with activity: Extracted from kebab (shown inline)
control_notes: Extracted when notes existcontrol_chat: Extracted when messages exist
- Remaining visible controls: Kebab menu if 2+, otherwise inline
Icon Categories:
Icons are split into two categories with independent overrides:
| Category | Controls | Override Prop |
|---|---|---|
| Edit Icons | control_edit_field, control_edit_group, control_delete_field |
show_edit_icons |
| Control Icons | control_data_ok, control_skip, control_notes, control_chat, control_lock, control_visibility, control_delete, control_private_files |
show_control_icons |
| Standalone | control_add_elements, control_edit_elements |
None (always role-driven) |
Groups use control_edit_group (not control_edit_field) for their edit pencil icon, allowing roles to independently control group vs field editing.
Field/Group Level Override:
Icons show automatically based on role config. Use show_edit_icons: false or show_control_icons: false to force-hide on specific fields/groups:
const fields: FieldConfig[] = [
{
id: "sensitive_group",
field_type: "group",
label: "Sensitive Information",
// Icons show automatically from role config - no show_icons needed
// Use overrides only to force-hide:
// show_edit_icons: false, // Hide edit/delete icons on this group
// show_control_icons: false, // Hide data_ok/notes/chat/etc on this group
sub_fields: [
{
id: "ssn",
label: "Social Security Number",
component_type: "HazoCollabFormInputbox",
value: ""
}
]
}
];Migration from
show_icons: Theshow_iconsprop is deprecated. Remove allshow_icons: truefrom your form configs — icons now show automatically from role config. If you usedshow_icons: falseto hide icons, replace withshow_edit_icons: falseand/orshow_control_icons: false.
Role-Level Permissions:
Private file access is controlled via the control_private_files control in the role's controls object:
{
role_id: 'tax_agent',
role_name: 'Tax Agent',
controls: {
control_private_files: { visible: true, enabled: true }, // Shows private file sections
// ... other controls
}
}When control_private_files.visible is true, users with that role will see private file upload sections on fields that have accept_files_private: true. This is automatically resolved from the active_role prop.
Benefits:
- Single configuration for entire form
- Consistent behavior across form and summary views
- Automatic UI optimization (kebab consolidation)
- Easy role switching without code changes
- Role-based permissions (private files, etc.)
- Visual editor in Template Generator
Dynamic Field Dialogs
Add, edit, and manage form fields at runtime using dialog components.
HazoAddFieldDialog:
Add new fields or groups to your form dynamically:
import { HazoAddFieldDialog, type NewFieldDefinition } from 'hazo_collab_forms';
import { useState } from 'react';
export default function DynamicForm() {
const [isOpen, setIsOpen] = useState(false);
const handleAddField = (field: NewFieldDefinition) => {
console.log('New field:', field);
// Add field to your form sections
};
return (
<>
<button onClick={() => setIsOpen(true)}>Add Field</button>
<HazoAddFieldDialog
open={isOpen}
on_open_change={setIsOpen}
on_add={handleAddField}
parent_type="group" // 'section' | 'group'
parent_id="personal_info"
/>
</>
);
}HazoEditFieldDialog:
Edit existing field configurations:
import { HazoEditFieldDialog } from 'hazo_collab_forms';
<HazoEditFieldDialog
open={isEditOpen}
on_open_change={setIsEditOpen}
field={selectedField}
on_save={(updated) => updateField(updated)}
on_delete={() => deleteField(selectedField.id)} // Optional delete callback
/>HazoFieldSelectorDialog:
Search and select fields from a template:
import { HazoFieldSelectorDialog } from 'hazo_collab_forms';
<HazoFieldSelectorDialog
open={isSelectorOpen}
on_open_change={setIsSelectorOpen}
template={formTemplate}
on_select={(selection) => {
// Handle selected fields
console.log('Selected:', selection);
}}
/>Shared Utilities
The package exports utility functions for working with form structures:
import {
// Field traversal
traverse_fields,
traverse_sections,
collect_field_ids,
collect_leaf_field_ids,
should_render_field,
is_field_empty,
get_field_value,
// Dependency checking
parse_dependency,
check_dependency,
// Field metadata normalization
normalize_field_metadata,
normalize_field_metadata_input,
// File management
get_field_attachments,
get_field_private_attachments,
is_form_file_attachment,
attachment_to_file_data,
file_data_to_attachment,
} from 'hazo_collab_forms';
// Traverse all fields in sections
traverse_sections(sections, (field, path) => {
console.log(`Field: ${field.id} at path: ${path.join('/')}`);
});
// Check if field should render based on dependencies
const shouldRender = should_render_field(field, formData, {
check_dependencies: true,
show_empty_fields: false,
});
// Parse and check field dependencies
const dependency = parse_dependency(field.depends_on);
const isActive = check_dependency(dependency, formData);
// Get file attachments for a field
const publicFiles = get_field_attachments(formData, 'document_field');
const privateFiles = get_field_private_attachments(formData, 'sensitive_field');Components
Form Field Components
| Component | Description | Required shadcn |
|---|---|---|
HazoCollabFormInputbox |
Text input with validation | button, label, dialog, tooltip |
HazoCollabFormTextArea |
Multi-line text input | button, label, dialog, tooltip |
HazoCollabFormCheckbox |
Boolean toggle | button, label, dialog, tooltip |
HazoCollabFormCombo |
Dropdown select with search | + popover, command |
HazoCollabFormRadio |
Radio button group | button, label, dialog, tooltip |
HazoCollabFormDate |
Date or date-range picker | + calendar |
HazoCollabFormDoc |
Document/file-only field | button, label, dialog, tooltip, accordion |
HazoCollabFormGroup |
Field grouping container | button, label, dialog, tooltip |
HazoCollabFormDataTable |
Editable data table with aggregations | button, label, dialog, tooltip |
Form Container Components
| Component | Description | Required shadcn |
|---|---|---|
HazoCollabFormView |
Recommended - Unified multi-mode view (Edit/Summary/Print/Approval) | all components |
HazoCollabFormSet |
⚠️ Deprecated - Use HazoCollabFormView with mode="edit" |
all components |
Builder & Library Components
| Component | Description | Required shadcn |
|---|---|---|
HazoFieldLibrary |
New - Database-backed field/group library manager | button, label, dialog, tooltip, tabs, popover, command |
HazoTemplateGenerator |
⚠️ Deprecated - Visual form builder | button, label, dialog, tooltip, tabs, resizable |
HazoAddFieldDialog |
Add new fields dynamically (with library search) | dialog, popover, command |
HazoAddGroupDialog |
Add new groups dynamically | dialog |
HazoEditFieldDialog |
Edit field configurations | dialog, popover, command |
HazoFieldSelectorDialog |
Search and select fields | dialog, command |
Optional Features
| Feature | Required shadcn |
|---|---|
File Upload (accept_files_public / accept_files_private props) |
+ accordion (required) |
Autofill Dropzone (autofill_dropzone group prop) |
requires file_manager + API endpoint |
Field Tooltips (tooltip prop) |
+ hover-card (optional) |
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_public or accept_files_private props 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