Entity Management Components
The CRUD bundle provides a comprehensive set of components for managing entity lifecycle operations including creation, editing, viewing, and deletion. These components work seamlessly with the DetailShowContext and form handling systems.
Core Architecture
DetailShowContext
Entity management components rely on the DetailShowContext for state management:
interface DetailShowContextProps {
// Data Management
data: any; // Current entity data
loading: boolean; // Loading state
fetchData: () => void; // Data fetching function
// Page Management
managingPage: boolean; // Page customization mode
setManagingPage: (value: boolean) => void; // Toggle page management
// Translations
managingTranslations: boolean; // Translation management mode
setManagingTranslations: (value: boolean) => void; // Toggle translation mode
// UI Configuration
fieldsConfiguration: any[]; // Field display configuration
sectionsConfiguration: any[]; // Section layout configuration
}Component Overview
1. AddEntityForm
Form component for creating new entities with comprehensive validation and field rendering.
Props
type AddEntityFormProps = {
formInputs: FormInput[]; // Form field definitions
fqcn_bui: IFQCN_BUI; // Component configuration
resource: string; // API resource name
translatable: boolean; // Enable multi-language support
kvs: {
// Key-Value Store integration
createKVS: any; // Create KVS function
kvs: any[]; // KVS data array
creatingKVS: boolean; // KVS creation loading state
loading: boolean; // KVS loading state
updateKVS: any; // Update KVS function
};
form: any; // React Hook Form instance
user: User | null; // Current user
csrfJwt: string; // CSRF token
tenant: string; // Tenant identifier
locale: string; // Locale for translations
};Form Input Configuration
interface FormInput {
title: string; // Section title
fields: FilterField[]; // Array of fields in this section
}Features
- Dynamic Field Rendering: Supports 15+ field types
- Real-time Validation: Zod schema validation with live feedback
- Form State Persistence: Auto-save form values to KVS
- Multi-language Support: Translatable field values
- Keyboard Shortcuts: Ctrl+S to save, Esc to cancel
- Dependent Fields: Fields that change based on other field values
- Conditional Display: Show/hide fields based on conditions
- File Upload: Integrated file upload with preview
Usage Example
import {
AddEntityForm,
ContextualAddPageFooter,
} from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
const formInputs: FormInput[] = [
{
title: "General",
fields: [
{
title: "Username",
key: "username",
type: ColumnTypeEnum.TEXT,
placeholder: "Input the Username",
required: true,
},
{
title: "Email",
key: "email",
type: ColumnTypeEnum.TEXT,
placeholder: "Input the Email",
required: true,
},
{
title: "Last Login",
key: "lastLogin",
type: ColumnTypeEnum.DATETIME,
placeholder: "Input the Last Login",
required: false,
},
{
title: "Landing Page Url",
key: "landingPageUrl",
type: ColumnTypeEnum.TEXT,
placeholder: "Input the Landing Page Url",
required: false,
},
{
title: "Email Auth Code Enabled",
key: "emailAuthCodeEnabled",
type: ColumnTypeEnum.BOOLEAN,
placeholder: "Select the Email Auth Code Enabled",
required: true,
},
{
title: "Is Verified",
key: "isVerified",
type: ColumnTypeEnum.BOOLEAN,
placeholder: "Select the Is Verified",
required: true,
},
{
title: "First Name",
key: "firstName",
type: ColumnTypeEnum.TEXT,
placeholder: "Input the First Name",
required: false,
},
{
title: "Last Name",
key: "lastName",
type: ColumnTypeEnum.TEXT,
placeholder: "Input the Last Name",
required: true,
},
{
title: "Phone Number",
key: "phoneNumber",
type: ColumnTypeEnum.TEXT,
placeholder: "Input the Phone Number",
required: false,
},
],
},
];
function CreateUserPage() {
const form = useForm({
resolver: zodResolver(validationSchema),
defaultValues: {
name: "",
email: "",
department_id: null,
avatar: null,
bio: "",
},
});
const handleSuccess = (data: any) => {
toast.success("User created successfully!");
router.push(`/users/${data.id}`);
};
return (
<div className="max-w-4xl mx-auto p-6">
<div className="bg-white rounded-lg shadow-md p-6">
<h1 className="text-2xl font-bold mb-6">Create New User</h1>
<AddEntityForm
formInputs={formInputs}
fqcn_bui={{ Bundle: "user", Unit: "management", Interface: "create" }}
resource="users"
translatable={true}
kvs={useKVS}
form={form}
kvsKey="user_create_form"
user={user}
tenant={tenant}
csrfJwt={csrfToken}
locale="en"
onSuccess={handleSuccess}
/>
<ContextualAddPageFooter
fqcn_bui={fqcn_bui}
resource="users"
kvs={useKVS}
form={form}
kvsKey="user_create_form"
user={user}
locale="en"
/>
</div>
</div>
);
}2. EditEntityForm
Form component for editing existing entities with pre-populated data and update functionality.
Props
type EditEntityFormProps = {
formInputs: FormInput[]; // Form field definitions
fqcn_bui: IFQCN_BUI; // Component configuration
resource: string; // API resource name
data: Record<string, any>; // Pre-populated entity data
translatable?: boolean; // Enable multi-language support
qField: string; // Query field for updates (usually "id")
csrfJwt: string; // CSRF token
tenant: string; // Tenant identifier
locale: string; // Locale for translations
};Features
- Pre-populated Fields: Automatically fills form with existing data
- Patch Updates: Only sends changed fields to API
- Optimistic Updates: UI updates before API confirmation
- Keyboard Shortcuts: Alt+Enter to save, Esc to cancel
- Dirty Field Tracking: Highlights modified fields
- Auto-save Draft: Periodically saves changes to prevent data loss
Usage Example
import { EditEntityForm } from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
function EditUserPage({ userId }: { userId: string }) {
const [userData, setUserData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Fetch user data
fetchUserData(userId).then((data) => {
setUserData(data);
setLoading(false);
});
}, [userId]);
if (loading) return <div>Loading...</div>;
return (
<div className="max-w-4xl mx-auto p-6">
<div className="bg-white rounded-lg shadow-md p-6">
<h1 className="text-2xl font-bold mb-6">Edit User</h1>
<EditEntityForm
formInputs={formInputs}
fqcn_bui={{ Bundle: "user", Unit: "management", Interface: "edit" }}
resource="users"
data={userData}
translatable={true}
qField="id"
csrfJwt={csrfToken}
tenant={tenant}
locale="en"
onSuccess={() => {
toast.success("User updated successfully!");
router.push(`/users/${userId}`);
}}
/>
</div>
</div>
);
}3. GeneralDetails
Component for displaying entity details in a structured, customizable layout with translation support.
Props
type GeneralDetailsProps = {
detailsInfo: { [key: string]: string }[]; // Details data array
resource: string; // API resource name
sectionTitle: string; // Section title
id?: number; // Entity ID
handleRemoveSection: (section: string) => void; // Remove section callback
handleRemoveField: (sectionTitle: string, field: string) => void; // Remove field callback
loadingKVS?: boolean; // KVS loading state
fqcn_bui: IFQCN_BUI; // Component configuration
locale: string; // Locale for translations
updateTranslations: (translations: { key?: string; value: string }) => void; // Update translations
user: User | null; // Current user
};Field Display Configuration
interface DetailField {
field: string; // Data field path (supports nested: "user.name")
label: string; // Display label
type: ColumnTypeEnum; // Display type
format?: string; // Format string (for dates, numbers)
render?: (value: any, data: any) => ReactNode; // Custom renderer
translatable?: boolean; // Can be translated
editable?: boolean; // Inline editing
copyable?: boolean; // Show copy button
linkable?: boolean; // Make clickable link
conditional?: {
// Conditional display
field: string;
value: any;
};
}Features
- Nested Field Support: Display nested object properties
- Custom Renderers: Custom display logic for specific fields
- Inline Translation: Edit translations directly from view
- Copy to Clipboard: Copy field values with single click
- Anchor Links: Automatic anchor links for sections
- Responsive Layout: Adapts to different screen sizes
- Field Management: Add/remove fields in management mode
Usage Example
import {
GeneralDetails,
DetailedShowContextProvider,
} from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
const detailsConfig = [
{
field: "id",
label: "User ID",
type: ColumnTypeEnum.NUMBER,
},
{
field: "name",
label: "Full Name",
type: ColumnTypeEnum.TEXT,
translatable: true,
},
{
field: "email",
label: "Email Address",
type: ColumnTypeEnum.EMAIL,
},
{
field: "created_at",
label: "Created Date",
type: ColumnTypeEnum.DATE,
},
];
function UserDetailsSection() {
const handleRemoveSection = (section: string) => {
// Handle section removal
};
const handleRemoveField = (sectionTitle: string, field: string) => {
// Handle field removal
};
const updateTranslations = (translations: any) => {
// Handle translation updates
};
return (
<DetailedShowContextProvider>
<GeneralDetails
detailsInfo={detailsConfig}
resource="users"
sectionTitle="General Information"
id={1}
handleRemoveSection={handleRemoveSection}
handleRemoveField={handleRemoveField}
fqcn_bui={fqcn_bui}
locale="en"
updateTranslations={updateTranslations}
user={user}
/>
</DetailedShowContextProvider>
);
}4. DetailShowHeader
Simple header component displaying entity title with loading state.
Props
interface DetailShowHeaderProps {
// Uses DetailShowContext automatically - no props needed
}Features
- Auto Title: Displays entity’s
toStringfield - Loading State: Shows skeleton while data loads
- Responsive Design: Adapts to container width
Usage Example
import { DetailShowHeader } from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
function UserDetailPage() {
return (
<DetailedShowContextProvider>
<div className="bg-white rounded-lg shadow-md">
<div className="p-6 border-b">
<DetailShowHeader />
</div>
{/* Rest of detail view */}
</div>
</DetailedShowContextProvider>
);
}5. ShowHeaderActions
Comprehensive action buttons for entity detail views with customizable actions.
Props
type ShowHeaderActionsProps = {
fqcn_bui: IFQCN_BUI; // Component configuration
resource: string; // API resource name
defaultActions: string[]; // Default enabled actions
fields?: DisplayField[]; // Field configuration for actions
user: User | null; // Current user
locale: string; // Locale for translations
tenant: string; // Tenant identifier
csrfJwt: string; // CSRF token
// Customization Options
customActions?: ActionButtonConfig[]; // Additional custom actions
customDropdowns?: ActionDropdownConfig[]; // Custom dropdown menus
hiddenActions?: ActionType[]; // Actions to hide
visibleActions?: ActionType[]; // Only show these actions
onCustomAction?: (actionType: string, id?: number) => void; // Custom action handler
showTooltips?: boolean; // Show action tooltips
containerClass?: string; // Custom container CSS
buttonSize?: "sm" | "md" | "lg"; // Button size
showDropdown?: boolean; // Show actions dropdown
};Available Actions
- Edit: Navigate to edit form
- Delete: Delete entity with confirmation
- Duplicate: Create copy of entity
- Print: Print current view
- Export: Export entity data
- Share: Share entity link
- Archive: Archive/unarchive entity
- Flag: Mark as featured/default
Custom Action Configuration
interface ActionButtonConfig {
type: string; // Action identifier
label: string; // Button label
icon: ReactNode; // Button icon
variant?: ButtonVariant; // Button style
onClick: (id?: number) => void; // Click handler
condition?: (data: any) => boolean; // Show condition
tooltip?: string; // Tooltip text
}
interface ActionDropdownConfig {
label: string; // Dropdown label
items: ActionButtonConfig[]; // Dropdown items
}Usage Example
import { ShowHeaderActions } from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
import { FiArchive, FiSend } from "react-icons/fi";
function UserDetailActions() {
const customActions: ActionButtonConfig[] = [
{
type: "archive",
label: "Archive User",
icon: <FiArchive />,
variant: "outline",
onClick: (id) => handleArchiveUser(id),
condition: (data) => data.status === "active",
tooltip: "Archive this user account",
},
{
type: "send_email",
label: "Send Email",
icon: <FiSend />,
variant: "default",
onClick: (id) => handleSendEmail(id),
tooltip: "Send email to user",
},
];
const customDropdowns: ActionDropdownConfig[] = [
{
label: "Admin Actions",
items: [
{
type: "reset_password",
label: "Reset Password",
icon: <FiKey />,
onClick: (id) => handleResetPassword(id),
},
{
type: "impersonate",
label: "Impersonate User",
icon: <FiUser />,
onClick: (id) => handleImpersonate(id),
},
],
},
];
return (
<ShowHeaderActions
fqcn_bui={fqcn_bui}
resource="users"
defaultActions={["edit", "delete", "duplicate"]}
user={user}
locale="en"
tenant={tenant}
csrfJwt={csrfToken}
customActions={customActions}
customDropdowns={customDropdowns}
hiddenActions={["archive"]} // Hide if using custom archive action
showTooltips={true}
buttonSize="md"
showDropdown={true}
onCustomAction={handleCustomAction}
/>
);
}6. ContextualAddPageFooter
Footer component for add/edit forms with save functionality and form state management.
Props
interface ContextualAddPageFooterProps {
fqcn_bui: IFQCN_BUI; // Component configuration
resource: string; // API resource name
kvs: {
// Key-Value Store integration
createKVS: any; // Create KVS function
kvs: any[]; // KVS data array
creatingKVS: boolean; // KVS creation loading state
loading: boolean; // KVS loading state
updateKVS: any; // Update KVS function
};
form: any; // React Hook Form instance
kvsKey: string; // KVS key for form persistence
user: User | null; // Current user
locale: string; // Locale for translations
}Features
- Auto-save: Automatically saves form values to KVS
- Print Support: Print current form state
- Settings Integration: Save form layout preferences
- Loading States: Visual feedback during operations
Complete Implementation Example
Here’s a complete example showing how to build a full entity management interface:
Full CRUD Interface
import React from 'react';
import { useRouter } from 'next/navigation';
import {
DetailedShowContextProvider,
DetailShowHeader,
ShowHeaderActions,
GeneralDetails,
AddEntityForm,
EditEntityForm,
ContextualAddPageFooter
} from "@phpcreation/frontend-crud-react-nextjs-bundle/components";
interface UserCRUDProps {
mode: 'create' | 'edit' | 'view';
userId?: string;
user: User | null;
tenant: string;
csrfJwt: string;
}
export default function UserCRUD({
mode,
userId,
user,
tenant,
csrfJwt
}: UserCRUDProps) {
const router = useRouter();
const [userData, setUserData] = useState(null);
const [loading, setLoading] = useState(mode !== 'create');
const fqcn_bui = {
Bundle: "user",
Unit: "management",
Interface: mode
};
// Fetch user data for edit/view modes
useEffect(() => {
if (mode !== 'create' && userId) {
fetchUserData(userId).then(data => {
setUserData(data);
setLoading(false);
});
}
}, [mode, userId]);
const formInputs: FormInput[] = [
{
key: "name",
label: "Full Name",
type: ColumnTypeEnum.TEXT,
required: true,
grid: { xs: 12, md: 6 }
},
{
key: "email",
label: "Email",
type: ColumnTypeEnum.EMAIL,
required: true,
grid: { xs: 12, md: 6 }
},
{
key: "department_id",
label: "Department",
type: ColumnTypeEnum.SELECT_ASYNC,
apiEndpoint: "/api/departments/dropdown",
grid: { xs: 12, md: 6 }
},
{
key: "status",
label: "Status",
type: ColumnTypeEnum.SELECT,
options: [
{label: "Active", value: "active"},
{label: "Inactive", value: "inactive"}
],
grid: { xs: 12, md: 6 }
}
];
const detailsConfig = [
{ field: "id", label: "User ID", type: ColumnTypeEnum.NUMBER },
{ field: "name", label: "Full Name", type: ColumnTypeEnum.TEXT },
{ field: "email", label: "Email", type: ColumnTypeEnum.EMAIL },
{ field: "department.name", label: "Department", type: ColumnTypeEnum.TEXT },
{ field: "status", label: "Status", type: ColumnTypeEnum.SELECT },
{ field: "created_at", label: "Created", type: ColumnTypeEnum.DATETIME }
];
if (loading) return <div>Loading...</div>;
return (
<div className="max-w-6xl mx-auto p-6">
<div className="bg-white rounded-lg shadow-md">
{/* Header */}
<div className="p-6 border-b flex justify-between items-center">
{mode === 'create' ? (
<h1 className="text-2xl font-bold">Create New User</h1>
) : (
<DetailedShowContextProvider>
<DetailShowHeader />
{mode === 'view' && (
<ShowHeaderActions
fqcn_bui={fqcn_bui}
resource="users"
defaultActions={["edit", "delete", "duplicate"]}
user={user}
locale="en"
tenant={tenant}
csrfJwt={csrfJwt}
/>
)}
</DetailedShowContextProvider>
)}
</div>
{/* Content */}
<div className="p-6">
{mode === 'create' && (
<>
<AddEntityForm
formInputs={formInputs}
fqcn_bui={fqcn_bui}
resource="users"
translatable={true}
kvs={useKVS}
form={form}
kvsKey="user_create_form"
user={user}
tenant={tenant}
csrfJwt={csrfJwt}
locale="en"
onSuccess={(data) => {
toast.success("User created successfully!");
router.push(`/users/${data.id}`);
}}
/>
<ContextualAddPageFooter
fqcn_bui={fqcn_bui}
resource="users"
kvs={useKVS}
form={form}
kvsKey="user_create_form"
user={user}
locale="en"
/>
</>
)}
{mode === 'edit' && userData && (
<EditEntityForm
formInputs={formInputs}
fqcn_bui={fqcn_bui}
resource="users"
data={userData}
translatable={true}
qField="id"
csrfJwt={csrfJwt}
tenant={tenant}
locale="en"
onSuccess={() => {
toast.success("User updated successfully!");
router.push(`/users/${userId}`);
}}
/>
)}
{mode === 'view' && (
<DetailedShowContextProvider>
<GeneralDetails
detailsInfo={detailsConfig}
resource="users"
sectionTitle="User Information"
handleRemoveSection={() => {}}
handleRemoveField={() => {}}
fqcn_bui={fqcn_bui}
locale="en"
updateTranslations={() => {}}
user={user}
/>
</DetailedShowContextProvider>
)}
</div>
</div>
</div>
);
}
Best Practices
Advanced Customization
Custom Field Types
// Register custom field renderer
const customFieldRenderer = (field: FormInput, form: any) => {
if (field.type === "color_picker") {
return (
<ColorPicker
value={form.watch(field.key)}
onChange={(color) => form.setValue(field.key, color)}
/>
);
}
return null;
};Custom Validation
const customValidationSchema = z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z.string().email("Invalid email format"),
department_id: z.number().min(1, "Department is required"),
custom_field: z
.string()
.refine((value) => validateCustomField(value), "Custom validation failed"),
});Related Documentation
- Form Components - RenderFormFields, TranslatableFields
- Context Providers - DetailShowContext
- Modal Components - QuickAddModal, QuickEdit
- Utility Functions - validationSchema, useKVS
This comprehensive documentation covers all entity management components with detailed props, usage examples, and advanced customization options.