# inertia-detail > Create the read-only detail view component for a Goravel entity. Uses useTranslation hook for i18n labels, formatted dates, status badges, and metadata section. - Author: Jeremiah Chienda - Repository: liwoo/goravel-inertia-tw-starter - Version: 20260208202531 - Stars: 10 - Forks: 8 - Last Updated: 2026-02-08 - Source: https://github.com/liwoo/goravel-inertia-tw-starter - Web: https://mule.run/skillshub/@@liwoo/goravel-inertia-tw-starter~inertia-detail:20260208202531 --- --- name: inertia-detail description: Create the read-only detail view component for a Goravel entity. Uses useTranslation hook for i18n labels, formatted dates, status badges, and metadata section. argument-hint: "[EntityName]" allowed-tools: Read, Write, Edit, Grep, Glob --- # Inertia Detail View Component (i18n-aware) Create detail view for `$ARGUMENTS`. ## File Location `resources/js/pages//sections/DetailView.tsx` ## Complete Template ```tsx import React from 'react'; import { useTranslation } from 'react-i18next'; import { Calendar, User, FileText, Tag, Hash, CheckCircle, XCircle, Clock } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; import { Separator } from '@/components/ui/separator'; import { CrudDetailViewProps } from '@/types/crud'; import { Entity } from '@/types/entity'; export function EntityDetailView({ item: entity, onEdit, onClose, canEdit, }: CrudDetailViewProps) { const { t } = useTranslation('entities'); const formatDate = (date: string | Date | null | undefined) => { if (!date) return t('form.notSpecified'); return new Date(date).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric', }); }; const getStatusBadge = (status: string) => { const statusConfig: Record = { 'ACTIVE': { color: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400', icon: , label: t('status.active'), }, 'INACTIVE': { color: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400', icon: , label: t('status.inactive'), }, }; const config = statusConfig[status] || { color: 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-400', icon: null, label: t('status.unknown'), }; return ( {config.icon} {config.label} ); }; return (
{/* Main Information Section */}

{t('form.entityInfo')}

{/* Text Field */}

{t('form.name').replace(' *', '')}

{entity.name}

{/* Enum/Status Field with Badge */} {/*

{t('form.status')}

{getStatusBadge(entity.status)}
*/} {/* Long Text Field */}

{t('form.description')}

{entity.description || ( {t('form.notSpecified')} )}

{/* Array/Tags Field */} {/*

{t('form.tags')}

{entity.tags?.map((tag, i) => ( {tag} )) || ( {t('form.notSpecified')} )}
*/}
{/* Metadata Section */}

{t('form.metadata')}

{t('form.entityId')}

#{entity.id}

{t('form.created')}

{formatDate(entity.createdAt || entity.created_at)}

{t('form.lastUpdated')}

{formatDate(entity.updatedAt || entity.updated_at)}

); } ``` ## i18n Pattern: `useTranslation` in Detail Views Detail views are React components (not plain functions), so they use the hook directly: ```tsx const { t } = useTranslation('entities'); ``` ### Label Reuse from Forms Detail views reuse `form.*` keys (minus the `*` suffix for required markers): ```tsx

{t('form.name').replace(' *', '')}

``` ### Status Badges Build status config inline using `t('status.*')` keys: ```tsx const statusConfig = { 'ACTIVE': { label: t('status.active'), ... }, 'INACTIVE': { label: t('status.inactive'), ... }, }; ``` ### Empty Values ```tsx {entity.field || ( {t('form.notSpecified')} )} ``` ## Required Translation Keys ```json { "form": { "entityInfo": "Entity Information", "metadata": "Metadata", "name": "Name *", "description": "Description", "status": "Status", "entityId": "Entity ID", "created": "Created", "lastUpdated": "Last Updated", "notSpecified": "Not specified" }, "status": { "active": "Active", "inactive": "Inactive", "unknown": "Unknown" } } ``` ## Price/Currency Display Pattern For decimal/currency fields, always format to avoid floating-point precision artifacts: ```tsx const formatCurrency = (value: number | undefined | null) => { if (value == null) return t('form.notSpecified'); return `$${value.toFixed(2)}`; }; // Or locale-aware: const formatCurrency = (value: number | undefined | null) => { if (value == null) return t('form.notSpecified'); return value.toLocaleString('en-US', { style: 'currency', currency: 'USD' }); }; ``` **Never display raw `float64` values** — they may show `23.989999771118164` instead of `23.99`. ## Key Differences from Forms - **Functional component** (NOT forwardRef) - **Read-only** — no inputs, just display - Uses `CrudDetailViewProps` interface - Same icon layout pattern as forms for visual consistency - Always include Metadata section with ID, Created, Updated ## Verify After creating the detail view: ```bash # TypeScript compiles npx tsc --noEmit # Lint the detail view npx eslint "resources/js/pages//sections/DetailView.tsx" --max-warnings=0 ``` ## Reference See `resources/js/pages/Books/sections/BookDetailView.tsx` for a complete i18n-aware example.