Skip to Content
FrontendBundlesCRUDEntity Management Components

Entity Management Components

Components for the full entity lifecycle: create, edit, view, and delete. Uses DetailShowContext for shared state on detail/show pages.


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 toString field
  • 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:

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> ); }

Last updated on