import type { ApiWithParams, PaginationListColumnType } from '@/types' import { removeEmptyFields, coerceToString } from '@/helpers' export const ALL_FILTER_OPTION_CODE = '__all__' export type PaginatedListFilterValue = string | number | { from?: string; to?: string } export type PaginatedListFilters = Record export type PaginatedListRow = Record & { id?: string | number } /** Which filter control a column renders. The single source of truth for mapping `column.type` to filter behavior — both the inline table-header filter and the modal accordion filter dispatch off of this instead of each re-implementing the mapping. */ export type FilterFieldKind = 'scalar' | 'range' | 'select' | 'date' | 'dateRange' | 'none' export function getColumnFilterConfig(column: PaginationListColumnType): FilterFieldKind { if (!column.type || column.type === 'text' || column.type === 'number') return 'scalar' if (column.type === 'inputFromTo') return 'range' if (column.type === 'select') return column.filterItems?.length ? 'select' : 'none' if (column.type === 'date') return 'date' if (column.type === 'dateFromTo') return 'dateRange' return 'none' } export function toDatePickerValue(value?: string): Date | null { if (!value) return null const date = new Date(value) return Number.isFinite(date.getTime()) ? date : null } export function toSelectFilterSelectedKeys(value: unknown): string[] { return value == null || value === '' ? [ALL_FILTER_OPTION_CODE] : [coerceToString(value)] } export function withAllFilterOption(items: { code: string; name: string }[]) { return items.some((item) => item.code === ALL_FILTER_OPTION_CODE) ? items : [{ code: ALL_FILTER_OPTION_CODE, name: 'همه' }, ...items] } export function isAllFilterValue(value: string): boolean { return value === ALL_FILTER_OPTION_CODE || value === '' } export function getFilterTextValue(value?: PaginatedListFilterValue): string { return value == null || typeof value === 'object' ? '' : coerceToString(value) } export function getNestedFilterValue(value: PaginatedListFilterValue | undefined, field: 'from' | 'to'): string | undefined { return value && typeof value === 'object' ? value[field] : undefined } export function getFilterDateValue(value?: PaginatedListFilterValue): string | undefined { return typeof value === 'string' ? value : undefined } export function getActiveSortColumn(sort: string): string | null { return sort ? (sort.startsWith('-') ? sort.slice(1) : sort) : null } export function withRowKeys(items: T[]): T[] { return items.map((item, index) => { const row = item as Record const customer = row.customer const customerId = customer && typeof customer === 'object' && 'id' in customer ? (customer as Record).id : undefined const id = row.id ?? customerId return { ...row, id: id != null && id !== '' ? coerceToString(id) : `row-${index}` } as T }) } export function serializeFilters(filters: PaginatedListFilters): Record { const result: Record = {} Object.entries(filters).forEach(([key, value]) => { if (value === 'null' || value === '') return if (value && typeof value === 'object') { const serialized = `${value.from ?? ''},${value.to ?? ''}` if (serialized !== ',') result[key] = serialized } else { result[key] = coerceToString(value) } }) return result } export function buildListParams({ filterPrefix, filters, page, resultType, rowsPerPage, sort, urlParams, }: { filterPrefix?: string filters: PaginatedListFilters page: number resultType?: number rowsPerPage: number sort: string urlParams: ApiWithParams }) { const cleanUrlParams = removeEmptyFields(urlParams) const inheritedFilters = cleanUrlParams.filters && typeof cleanUrlParams.filters === 'object' && !Array.isArray(cleanUrlParams.filters) ? (cleanUrlParams.filters as Record) : {} let params: Record = { ...cleanUrlParams, page, pageSize: rowsPerPage, filters: { ...inheritedFilters, ...serializeFilters(filters) }, sort, ...(resultType === undefined ? {} : { resultType }), } if (filterPrefix) { params = Object.fromEntries(Object.entries(params).map(([key, value]) => [`${filterPrefix}.${key}`, value])) } return removeEmptyFields(params) }