Files
road-monitoring-ui/src/components/lookups/RoleCombobox.tsx

79 lines
2.0 KiB
TypeScript

'use client';
import { AsyncCombobox, usePaginatedLookup } from '@/components/async-combobox';
import type { AsyncComboboxOption } from '@/components/async-combobox';
import { roleService } from '@/services/api';
import type { Role } from '@/types';
import { roleKeys } from '@/app/(modules)/roles/queries/roleKeys';
import type { RoleComboboxProps } from './RoleCombobox.types';
const ROLE_PAGE_SIZE = 20;
function parseRoleId(value: string) {
const roleId = Number(value);
return Number.isFinite(roleId) ? roleId : null;
}
function mapRoleOption(role: Role): AsyncComboboxOption {
return {
value: String(role.id),
label: role.display_name || role.name,
};
}
export function RoleCombobox({
value,
onValueChange,
enabled = true,
disabled = false,
portalContainer,
}: RoleComboboxProps) {
const lookup = usePaginatedLookup<Role>({
enabled,
selectedValue: value,
pageSize: ROLE_PAGE_SIZE,
queryKey: (searchTerm) =>
[
...roleKeys.lists(),
'async-lookup',
searchTerm,
ROLE_PAGE_SIZE,
] as const,
queryFn: ({ searchTerm, skip, limit }) =>
roleService.getRoles({
search_term: searchTerm || undefined,
skip,
limit,
effective_status: true,
sort_by: 'display_name',
sort_order: 'asc',
}),
mapOption: mapRoleOption,
resolveSelected: (roleId) => {
const numericRoleId = parseRoleId(roleId);
return numericRoleId
? roleService.getRoleById(numericRoleId)
: Promise.resolve(null);
},
resolveSelectedQueryKey: (roleId) => {
const numericRoleId = parseRoleId(roleId);
return numericRoleId
? roleKeys.detail(numericRoleId)
: [...roleKeys.details(), 'invalid', roleId];
},
});
return (
<AsyncCombobox
lookup={lookup}
onValueChange={onValueChange}
disabled={disabled}
placeholder="Select role"
searchPlaceholder="Search roles..."
emptyMessage="No roles found."
portalContainer={portalContainer}
/>
);
}