Proje: Okul.com.tr CRM · Hub: Okul.com.tr CRM — Conventions
CRM Liste Sayfası Pattern
Bileşenler
useListFiltershook — URL senkronizasyonu, filtreler, sayfalama, sıralamaGenericListPage— Tablo, filtreler, pagination, arama render’ıGenericDeleteDialog— Silme onay dialog’u
Temel Şablon
const DEFAULT_SORT: SortState = { column: "id", direction: "desc" };
const FILTER_KEY_MAPPINGS: Record<string, string> = {
uiFiltreAdi: 'api-filtre-adi'
};
export default function EntityPage() {
const [items, setItems] = useState<Entity[]>([]);
const [loading, setLoading] = useState(true);
const [totalItems, setTotalItems] = useState(0);
const [lastPage, setLastPage] = useState(1);
const initialFilters: GenericFilter[] = useMemo(() => [...], [referenceData]);
const { page, pageCount, sort, handlers, buildApiParams, ... } = useListFilters({
initialFilters,
defaultSort: DEFAULT_SORT,
filterKeyMappings: FILTER_KEY_MAPPINGS,
});
const fetchData = useCallback(async () => {
setLoading(true);
const params = buildApiParams({ 'includes[]': ['relation'] });
const response = await EntityService.getWithParams(params);
// set items, totalItems, lastPage
}, [buildApiParams]); // SADECE buildApiParams dep
useEffect(() => { fetchData(); }, [fetchData]);
return (
<GenericListPage
data={{ items, loading, error, totalItems }}
columns={columns}
search={page}
currentPage={page}
pageCount={pageCount}
sort={sort}
appliedFilters={appliedFilters}
onSearch={handlers.onSearch}
onPageChange={handlers.onPageChange}
// ...tüm handlers spread edilir
/>
);
}useListFilters Çıktıları
| Alan | Açıklama |
|---|---|
search, page, pageCount, sort | URL’den gelen değerler |
appliedFilters | URL’deki aktif filtreler (key-value map) |
filters / setFilters | FilterSheet için lokal state |
handlers | Tüm event handler’lar (onSearch, onPageChange, onSort…) |
buildApiParams() | API çağrısı için param objesi oluşturur |
displayFilters | UI’da gösterilen aktif filtre badge’leri |
isFilterSidebarOpen | FilterSheet açık/kapalı |
buildApiParams Ne Döner
{
page: urlPage,
per_page: urlPageCount,
sort: direction === 'desc' ? `-${column}` : column,
search: urlSearch, // varsa
'filter[apiKey]': value, // her aktif filtre için
...baseParams // ekstra geçirilen params
}filterKeyMappings Kullanımı
UI’daki filtre adını API’nin beklediği query param adına çevirir:
const FILTER_KEY_MAPPINGS = {
'city': 'location-1', // UI'da 'city' → API'de 'filter[location-1]'
'district': 'location-2',
'school_name': 'school-name'
};Hiyerarşik Filtreler
useListFilters hierarchicalFilters seçeneği destekler:
hierarchicalFilters: [{
parent: 'city',
child: 'district',
loadChildren: async (cityId) => await LocationService.getDistricts(cityId),
placeholder: 'Önce şehir seçin'
}]Parent seçildiğinde child options otomatik yüklenir, parent temizlendiğinde child sıfırlanır.
Referans Veri Yükleme
Filtre seçenekleri için referans veriler loadReferenceData callback’inde Promise.all ile paralel yüklenir. initialFilters bu verilere useMemo ile bağlıdır.
Related
- servis-katmani — Servis pattern ve getWithParams
- url-tabanli-filtreleme — useUrlFilters alt katmanı