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
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>
);
}
Related Documentation
- Form Components - RenderFormFields, TranslatableFields
- Context Providers - DetailShowContext
- Modal Components - QuickAddModal, QuickEdit
- Utility Functions - validationSchema, useKVS