import React, { useMemo, useState, useCallback, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { FaPlus, FaSearch, FaSync, FaClone, FaEye, FaFileExport } from 'react-icons/fa'; import { useProjectTemplates } from '../hooks/useProject'; import DynamicExportModal from '../components/DynamicExportModal'; import { fetchAllRowsForExport } from '../utils/frappeListExport'; import { useListPageSelection } from '../hooks/useListPageSelection'; const PAGE_SIZE = 20; const ProjectTemplateList: React.FC = () => { const { t } = useTranslation(); const navigate = useNavigate(); const [search, setSearch] = useState(''); const [page, setPage] = useState(0); const [showExportModal, setShowExportModal] = useState(false); const apiFilters = useMemo(() => { const f: Record = {}; if (search.trim()) f.name = ['like', `%${search.trim()}%`]; return f; }, [search]); const { templates, loading, totalCount, refetch } = useProjectTemplates({ filters: apiFilters, limit_start: page * PAGE_SIZE, limit_page_length: PAGE_SIZE, order_by: 'name asc', }); // Ensure pagination always triggers data reload (some environments cache identical queries). useEffect(() => { refetch(); }, [page, apiFilters, refetch]); const selectionResetKey = useMemo(() => `${page}|${JSON.stringify(apiFilters)}`, [page, apiFilters]); const { selectedRows, toggleRow, toggleAllOnPage, allOnPageSelected, someOnPageSelected, } = useListPageSelection(templates, selectionResetKey); const fetchAllForExport = useCallback( () => fetchAllRowsForExport({ doctype: 'Project Template', filters: apiFilters, orderBy: 'name asc' }), [apiFilters], ); const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); return (
/

{t('projects.projectTemplateDoctype')}

{ setSearch(e.target.value); setPage(0); }} placeholder="Search template…" className="w-full pl-9 pr-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white" />
{totalCount} total
setShowExportModal(false)} doctype="Project Template" selectedCount={selectedRows.size} pageCount={templates.length} totalCount={totalCount} pageData={templates} selectedRows={selectedRows} rowKey="name" onFetchAll={fetchAllForExport} fileNamePrefix="project_templates" />
{loading ? ( ) : templates.length === 0 ? ( ) : ( templates.map((row) => ( navigate(`/projects/templates/${encodeURIComponent(row.name)}`)} > )) )}
{ if (el) el.indeterminate = someOnPageSelected; }} onChange={toggleAllOnPage} aria-label="Select all on page" /> Name Project type Modified
Loading…
No templates found
e.stopPropagation()}> toggleRow(row.name)} aria-label={`Select ${row.name}`} /> {row.name} {row.project_type || '—'} {row.modified ? new Date(row.modified).toLocaleDateString() : '—'}
{totalCount > PAGE_SIZE && (
Page {page + 1} of {totalPages}
)}
); }; export default ProjectTemplateList;