/** * cli:scaffold-component — render/reconduction.ts * Extracted VERBATIM from the historical generate.ts (pure move — frozen by * the 1.0 corpus hash). Renders nothing when the view is absent from * spec.views. */ import type { GeneratedFile } from '../types.js' import { extensionsModuleId } from '../../../../../../lib/app-classification.js' import { GENERATED_MARKER } from './shared.js' import type { RenderContext } from './context.js' export function renderReconduction(rc: RenderContext): GeneratedFile[] { const files: GeneratedFile[] = [] const { spec, e, eLower, featurePath, pathFor, permKey, plural, sectionCamel } = rc // ─── ReconductionPage (Bug 7) ──────────────────────────────────────────── // Bulk-renewal pattern: shows eligible entities in a multi-select table with // a single "Reconduct selected" header action. Optional twin action "Refuse" // when reconductionConfig.withRefuse is true. The bulk hooks must exist in // api-client output (declare customActions `reconduct` and `refuse-reconduction` // with scope:'bulk' to wire the buttons). if (spec.views.includes('reconduction')) { const rcfg = spec.reconductionConfig ?? { actionLabelKey: 'reconduction.action.reconduct', withRefuse: false } const refuseImport = rcfg.withRefuse ? `, useRefuseReconduction${plural}` : '' const refuseHookDecl = rcfg.withRefuse ? ` const refuseMutation = useRefuseReconduction${plural}()\n` : '' const refuseHandler = rcfg.withRefuse ? ` const handleRefuse = async () => { if (selectedIds.length === 0) return await refuseMutation.mutateAsync(selectedIds) setSelectedIds([]) refetch() }` : '' const refuseButton = rcfg.withRefuse ? ` ` : '' files.push({ path: pathFor('reconduction', `${e}ReconductionPage.tsx`), content: `${GENERATED_MARKER}import { useState } from 'react' import { useTranslation } from 'react-i18next' import { Loader2, RefreshCw } from 'lucide-react' import { Slot } from '@atlashub/smartstack' import { PermissionGuard } from '@/components/auth/PermissionGuard' import { PageTemplate } from '@/components/ui/PageTemplate' import { routes } from '@/extensions/${extensionsModuleId(spec.appCode, spec.module)}Routes' import { use${plural}, useReconduct${plural}${refuseImport} } from '${featurePath}/hooks/use${e}' import type { ${e}ListDto } from '${featurePath}/types' export function ${e}ReconductionPage() { const { t } = useTranslation('${spec.module}') const { data, isLoading, error, refetch } = use${plural}() const reconductMutation = useReconduct${plural}() ${refuseHookDecl} const [selectedIds, setSelectedIds] = useState([]) const toggle = (id: string) => setSelectedIds((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id], ) const handleReconduct = async () => { if (selectedIds.length === 0) return await reconductMutation.mutateAsync(selectedIds) setSelectedIds([]) refetch() }${refuseHandler} const items: ${e}ListDto[] = data?.items ?? [] return ( } breadcrumbs={[ { label: t('${eLower}.breadcrumb.section'), href: routes.${sectionCamel}.list() }, { label: t('${eLower}.reconduction.title') }, ]} actions={
${refuseButton}
} > {error && (
{t('${eLower}.reconduction.error')}: {String(error)}
)} {isLoading ? (
{t('${eLower}.reconduction.loading')}
) : items.length === 0 ? (
{t('${eLower}.reconduction.empty')}
) : (
{items.map((item) => ( ))}
0} onChange={() => setSelectedIds(selectedIds.length === items.length ? [] : items.map((x) => x.id)) } aria-label={t('${eLower}.reconduction.selectAll')} /> {t('${eLower}.reconduction.columns.identifier')}
toggle(item.id)} aria-label={item.id} /> {item.id}
)}
) } export default ${e}ReconductionPage `, }) } return files }