Modal Components
The CRUD bundle provides a comprehensive set of modal dialog components for various user interactions including confirmations, quick actions, data imports, and content management. All modals use the DefaultModalSkin wrapper for consistent styling and behavior.
Core Architecture
DefaultModalSkin
All modal components use the DefaultModalSkin wrapper which provides consistent styling and behavior:
interface DefaultModalSkinProps {
title?: string | React.ReactNode; // Modal title
isOpen: boolean; // Controls modal visibility
onClose: () => void; // Close handler
children: React.ReactNode; // Modal content
isModal?: boolean; // Modal behavior (default: true)
locale?: string; // Locale for translations
}Features
- Accessible: Full ARIA support and keyboard navigation
- Responsive: Mobile-friendly design
- Customizable: Support for custom titles and content
- Auto-focus: Manages focus for accessibility
- Overlay: Click outside to close
- Scrollable: Handles overflow content
Component Overview
1. ConfirmationModal
Simple confirmation dialog for destructive or important actions.
Props
type ConfirmationModalProps = {
resource: string; // API resource name
isOpen: boolean; // Modal visibility
onClose: () => void; // Close handler
action: string; // Action type (delete, archive, etc.)
id?: number; // Target item ID
onConfirm: () => void; // Confirmation handler
locale?: string; // Locale for translations
}Features
- Customizable Actions: Support for any action type
- Icon Integration: Dynamic icons based on action
- Color Coding: Appropriate colors for different actions
- Keyboard Support: Enter to confirm, Esc to cancel
Usage Example
import { ConfirmationModal } from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
function UserManagement() {
const [deleteModal, setDeleteModal] = useState({
isOpen: false,
userId: null
});
const handleDelete = (userId: number) => {
setDeleteModal({
isOpen: true,
userId: userId
});
};
const confirmDelete = async () => {
try {
await deleteUser(deleteModal.userId);
toast.success("User deleted successfully!");
setDeleteModal({ isOpen: false, userId: null });
refetchUsers();
} catch (error) {
toast.error("Failed to delete user");
}
};
return (
<>
<Button
variant="destructive"
onClick={() => handleDelete(user.id)}
>
Delete User
</Button>
<ConfirmationModal
resource="users"
isOpen={deleteModal.isOpen}
onClose={() => setDeleteModal({ isOpen: false, userId: null })}
action="delete"
id={deleteModal.userId}
onConfirm={confirmDelete}
locale="en"
/>
</>
);
}2. QuickAddModal
Modal for quickly adding new entities without leaving the current page.
Props
type QuickAddModalProps = {
resource: string; // API resource name
onClose: () => void; // Close handler
isOpen: boolean; // Modal visibility
formInputs: FilterField[]; // Form field definitions
locale?: string; // Locale for translations
csrfJwt: string; // CSRF token
tenant: string; // Tenant identifier
}Features
- Dynamic Form: Renders form based on field configuration
- Real-time Validation: Live form validation with error display
- Auto-refresh: Updates parent listing after successful creation
- Keyboard Shortcuts: Ctrl+Enter to submit
- Form Reset: Clears form after successful submission
Usage Example
import {
QuickAddModal,
ListingContextProvider
} from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
const quickAddFields: FilterField[] = [
{
key: "name",
label: "Name",
type: ColumnTypeEnum.TEXT,
required: true,
placeholder: "Enter name"
},
{
key: "email",
label: "Email",
type: ColumnTypeEnum.EMAIL,
required: true,
placeholder: "user@example.com"
},
{
key: "status",
label: "Status",
type: ColumnTypeEnum.SELECT,
options: [
{label: "Active", value: "active"},
{label: "Inactive", value: "inactive"}
],
defaultValue: "active"
}
];
function UserListing() {
const [quickAddOpen, setQuickAddOpen] = useState(false);
return (
<ListingContextProvider>
<div className="space-y-4">
<Button onClick={() => setQuickAddOpen(true)}>
Quick Add User
</Button>
<QuickAddModal
resource="users"
isOpen={quickAddOpen}
onClose={() => setQuickAddOpen(false)}
formInputs={quickAddFields}
csrfJwt={csrfToken}
tenant={tenant}
locale="en"
/>
{/* Your listing table here */}
</div>
</ListingContextProvider>
);
}3. QuickEdit
Modal for quickly editing existing entities inline within listings.
Props
interface QuickEditProps {
resource: string; // API resource name
onClose: () => void; // Close handler
isOpen: boolean; // Modal visibility
id: number; // Entity ID to edit
formInputs: FilterField[]; // Form field definitions
locale?: string; // Locale for translations
tenant: string; // Tenant identifier
csrfJwt: string; // CSRF token
}Features
- Pre-populated Fields: Automatically loads existing entity data
- Patch Updates: Only sends changed fields to reduce payload
- Real-time Updates: Updates listing table immediately after save
- Error Handling: Comprehensive error display and recovery
- Optimistic Updates: UI updates before API confirmation
Usage Example
import { QuickEdit } from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
function UserListingActions({ user }: { user: any }) {
const [quickEditOpen, setQuickEditOpen] = useState(false);
const editFields: FilterField[] = [
{
key: "name",
label: "Name",
type: ColumnTypeEnum.TEXT,
required: true
},
{
key: "status",
label: "Status",
type: ColumnTypeEnum.SELECT,
options: [
{label: "Active", value: "active"},
{label: "Inactive", value: "inactive"}
]
}
];
return (
<>
<Button
size="sm"
variant="outline"
onClick={() => setQuickEditOpen(true)}
>
Quick Edit
</Button>
<QuickEdit
resource="users"
isOpen={quickEditOpen}
onClose={() => setQuickEditOpen(false)}
id={user.id}
formInputs={editFields}
tenant={tenant}
csrfJwt={csrfToken}
locale="en"
/>
</>
);
}4. ImportModal
Modal for bulk data import with CSV/Excel file support and preview functionality.
Props
interface ImportModalProps {
resource: string; // API resource name
onClose: () => void; // Close handler
isOpen: boolean; // Modal visibility
fqcn_bui: IFQCN_BUI; // Component configuration
onImportComplete?: (data: any) => void; // Success callback
qField?: string; // Query field for updates
locale?: string; // Locale for translations
user?: User; // Current user
csrfJwt: string; // CSRF token
tenant?: string; // Tenant identifier
}Features
- File Upload: Drag & drop or click to upload
- Format Support: CSV and Excel files
- Data Preview: Table preview before import
- Validation: Real-time data validation
- Progress Tracking: Import progress indicator
- Error Reporting: Detailed error messages for failed imports
- Batch Processing: Handles large datasets efficiently
Usage Example
import { ImportModal } from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
function UserImport() {
const [importModalOpen, setImportModalOpen] = useState(false);
const handleImportComplete = (result: any) => {
toast.success(`Successfully imported ${result.success_count} users`);
if (result.error_count > 0) {
toast.warning(`${result.error_count} imports failed`);
}
// Refresh the listing
refetchUsers();
};
return (
<>
<Button onClick={() => setImportModalOpen(true)}>
Import Users
</Button>
<ImportModal
resource="users"
isOpen={importModalOpen}
onClose={() => setImportModalOpen(false)}
fqcn_bui={{
Bundle: "user",
Unit: "management",
Interface: "import"
}}
onImportComplete={handleImportComplete}
user={user}
csrfJwt={csrfToken}
tenant={tenant}
locale="en"
/>
</>
);
}5. TranslationModal
Modal for managing multi-language translations for translatable content.
Props
interface TranslationModalProps {
onClose: () => void; // Close handler
isOpen: boolean; // Modal visibility
word: string; // Text to translate
onSave?: (updatedTranslations: {[key: string]: string}) => void; // Save callback
locale: string; // Current locale
updateTranslations: (translations: {key?: string; value: string}) => void; // Update handler
user: User | null; // Current user
}Features
- Multi-language Support: Manage translations for multiple locales
- Dynamic Language List: Supports all configured languages
- Real-time Editing: Live translation editing
- Auto-save: Automatic saving of translation changes
- Key-Value Storage: Integrates with KVS for persistence
Usage Example
import { TranslationModal } from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
function TranslatableContent() {
const [translationModal, setTranslationModal] = useState({
isOpen: false,
word: ""
});
const openTranslationModal = (word: string) => {
setTranslationModal({
isOpen: true,
word: word
});
};
const updateTranslations = (translation: any) => {
// Handle translation update
console.log("Translation updated:", translation);
// Update your state or refetch data
};
return (
<>
<div className="translatable-content">
<span
onClick={() => openTranslationModal("Welcome Message")}
className="cursor-pointer hover:bg-yellow-100"
>
{t("Welcome Message")}
<HiOutlineTranslate className="inline ml-1" />
</span>
</div>
<TranslationModal
isOpen={translationModal.isOpen}
onClose={() => setTranslationModal({ isOpen: false, word: "" })}
word={translationModal.word}
locale="en"
updateTranslations={updateTranslations}
user={user}
/>
</>
);
}6. ImageModal
Modal for viewing and managing images with full-screen display.
Props
interface ImageModalProps {
isOpen: boolean; // Modal visibility
onClose: () => void; // Close handler
imageUrl: string; // Image URL to display
imageAlt?: string; // Alt text for accessibility
title?: string; // Modal title
actions?: React.ReactNode; // Custom action buttons
}Features
- Full-screen View: Large image display with zoom
- Navigation: Previous/next for image galleries
- Download: Direct image download
- Responsive: Mobile-optimized viewing
- Accessibility: Full screen reader support
Usage Example
import { ImageModal } from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
function ProductGallery({ product }: { product: any }) {
const [imageModal, setImageModal] = useState({
isOpen: false,
imageUrl: "",
title: ""
});
const openImageModal = (imageUrl: string, title: string) => {
setImageModal({
isOpen: true,
imageUrl,
title
});
};
return (
<>
<div className="grid grid-cols-3 gap-4">
{product.images.map((image: any, index: number) => (
<img
key={index}
src={image.thumbnail}
alt={image.alt}
className="cursor-pointer rounded-lg hover:opacity-80"
onClick={() => openImageModal(image.full_size, image.title)}
/>
))}
</div>
<ImageModal
isOpen={imageModal.isOpen}
onClose={() => setImageModal({ isOpen: false, imageUrl: "", title: "" })}
imageUrl={imageModal.imageUrl}
title={imageModal.title}
imageAlt="Product image"
actions={
<Button variant="outline">
Download Image
</Button>
}
/>
</>
);
}7. HelpModal
Modal for displaying contextual help and documentation.
Props
interface HelpModalProps {
isOpen: boolean; // Modal visibility
onClose: () => void; // Close handler
title?: string; // Help section title
content: React.ReactNode; // Help content
searchable?: boolean; // Enable content search
locale?: string; // Locale for translations
}Features
- Rich Content: Support for markdown and HTML content
- Search: Find content within help documentation
- Navigation: Jump to specific sections
- Print Support: Print help content
- Responsive: Mobile-friendly help display
8. QuickShowModal
Modal for quickly previewing entity details without leaving the listing.
Props
type QuickShowModalProps = {
resource: string; // API resource name
onClose: () => void; // Close handler
isOpen: boolean; // Modal visibility
id: number; // Entity ID to preview
fields?: DisplayField[]; // Fields to display
locale?: string; // Locale for translations
tenant: string; // Tenant identifier
csrfJwt: string; // CSRF token
}9. DefaultFlagModal
Modal for setting an entity as the default or flagged item.
Props
type DefaultFlagModalProps = {
resource: string; // API resource name
isOpen: boolean; // Modal visibility
onClose: () => void; // Close handler
id: number; // Entity ID
currentDefault?: number; // Current default ID
onConfirm: () => void; // Confirmation handler
locale?: string; // Locale for translations
}10. AddFilterModal
Modal for adding and configuring custom filters.
Props
type AddFilterModalProps = {
isOpen: boolean; // Modal visibility
onClose: () => void; // Close handler
availableFields: FilterField[]; // Available filter fields
onAddFilter: (filter: FilterField) => void; // Add filter callback
locale?: string; // Locale for translations
}11. FullDuplicateModal
Modal for duplicating an entity with all its related data.
Props
type FullDuplicateModalProps = {
resource: string; // API resource name
isOpen: boolean; // Modal visibility
onClose: () => void; // Close handler
id: number; // Entity ID to duplicate
includeRelations?: boolean; // Include related entities
onSuccess?: (newId: number) => void; // Success callback
locale?: string; // Locale for translations
tenant: string; // Tenant identifier
csrfJwt: string; // CSRF token
}12. DetailedPrintModal
Modal for configuring and generating print output.
Props
type DetailedPrintModalProps = {
isOpen: boolean; // Modal visibility
onClose: () => void; // Close handler
data: any; // Data to print
template?: string; // Print template
orientation?: 'portrait' | 'landscape'; // Page orientation
locale?: string; // Locale for translations
}13. DetailedEmailModal
Modal for composing and sending emails related to an entity.
Props
type DetailedEmailModalProps = {
isOpen: boolean; // Modal visibility
onClose: () => void; // Close handler
resource: string; // API resource name
id: number; // Entity ID
defaultRecipients?: string[]; // Default email recipients
onSend?: (emailData: any) => void; // Send callback
locale?: string; // Locale for translations
tenant: string; // Tenant identifier
csrfJwt: string; // CSRF token
}14. EditListingImportModal
Modal for editing import configurations during the import process.
Props
type EditListingImportModalProps = {
isOpen: boolean; // Modal visibility
onClose: () => void; // Close handler
importConfig: ImportConfig; // Current import configuration
onSave: (config: ImportConfig) => void; // Save callback
locale?: string; // Locale for translations
}15. FormFieldsRenderer
A modal component that renders form fields dynamically.
Props
type FormFieldsRendererProps = {
isOpen: boolean; // Modal visibility
onClose: () => void; // Close handler
fields: FilterField[]; // Fields to render
onSubmit: (data: any) => void; // Submit callback
title?: string; // Modal title
locale?: string; // Locale for translations
}Modal Component Summary
| Modal | Purpose | Key Features |
|---|---|---|
| ConfirmationModal | Confirm actions | Customizable actions, keyboard support |
| QuickAddModal | Create entities | Dynamic forms, real-time validation |
| QuickEdit | Edit entities | Pre-populated fields, patch updates |
| QuickShowModal | Preview entities | Fast preview without navigation |
| ImportModal | Bulk import | CSV/Excel support, validation |
| TranslationModal | Manage translations | Multi-language support |
| ImageModal | View images | Full-screen, zoom, download |
| HelpModal | Show help | Searchable content |
| DefaultFlagModal | Set defaults | Flag/unflag entities |
| AddFilterModal | Add filters | Custom filter configuration |
| FullDuplicateModal | Duplicate entities | Include/exclude relations |
| DetailedPrintModal | Print configuration | Templates, orientation |
| DetailedEmailModal | Send emails | Recipients, templates |
| EditListingImportModal | Edit imports | Modify import configuration |
| FormFieldsRenderer | Dynamic forms | Render any field configuration |
Advanced Usage Patterns
Modal Composition
Combine multiple modals for complex workflows:
Sequential Modals
function UserWorkflow() {
const [currentModal, setCurrentModal] = useState<'add' | 'confirm' | 'success' | null>(null);
const [newUserId, setNewUserId] = useState<number | null>(null);
const handleUserCreated = (userData: any) => {
setNewUserId(userData.id);
setCurrentModal('confirm');
};
const handleConfirmComplete = () => {
setCurrentModal('success');
};
return (
<>
<Button onClick={() => setCurrentModal('add')}>
Add User
</Button>
<QuickAddModal
resource="users"
isOpen={currentModal === 'add'}
onClose={() => setCurrentModal(null)}
formInputs={userFields}
onSuccess={handleUserCreated}
csrfJwt={csrfToken}
tenant={tenant}
/>
<ConfirmationModal
resource="users"
isOpen={currentModal === 'confirm'}
onClose={() => setCurrentModal(null)}
action="send_welcome_email"
id={newUserId}
onConfirm={handleConfirmComplete}
/>
<DefaultModalSkin
isOpen={currentModal === 'success'}
onClose={() => setCurrentModal(null)}
title="Success"
>
<div className="text-center py-8">
<IoCheckmarkCircleOutline className="mx-auto text-green-500 text-6xl mb-4" />
<h3 className="text-lg font-semibold mb-2">User Created Successfully!</h3>
<p className="text-gray-600">Welcome email has been sent.</p>
</div>
</DefaultModalSkin>
</>
);
}Best Practices
Custom Modal Creation
Create custom modals using the DefaultModalSkin wrapper:
import { DefaultModalSkin } from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
function CustomModal({ isOpen, onClose }: { isOpen: boolean, onClose: () => void }) {
return (
<DefaultModalSkin
isOpen={isOpen}
onClose={onClose}
title="Custom Modal"
>
<div className="space-y-4">
<p>Your custom content here</p>
<div className="flex justify-end gap-2">
<Button variant="outline" onClick={onClose}>
Cancel
</Button>
<Button onClick={handleCustomAction}>
Confirm
</Button>
</div>
</div>
</DefaultModalSkin>
);
}This comprehensive documentation covers all modal components with their props, usage patterns, and advanced implementation examples.