badges: per-tier search limits — result cap + min chars, config UI
Add anonymous/auth/trusted search constraints to BadgesRemoteCfg with conservative defaults (anon: 15 results / 3 chars, auth: 25 / 2, trusted+: 150 / 1). Configurable per event via mod_badges_json. - BadgesRemoteCfg + BadgesLocState: 6 new fields with defaults - sync_config__event_badges: mirrors new fields from mod_badges_json - +page.svelte: effective_search_limits derived by tier using $ae_loc cumulative flags; enforces min_chars guard and result cap on both local IDB path and API call - ae_comp__badge_search: effective_min_chars derived same way; blocks search trigger below threshold; shows dynamic hint text - Fallback broad search (SCENARIO 2) suppressed for non-trusted users so no results show on page load without a query - config/+page.svelte: Search Limits section with 3-column number inputs (Anonymous / Auth / Trusted+) for result limit and min chars Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -111,8 +111,10 @@ let lq__event_badge_obj_li = $derived.by(() => {
|
||||
}
|
||||
|
||||
// SCENARIO 2: Fallback broad search (Only if no active filters)
|
||||
// Unauthenticated users must enter a query — never show the full attendee list.
|
||||
if (
|
||||
event_id &&
|
||||
$ae_loc.trusted_access &&
|
||||
!badges_loc.current.fulltext_search_qry_str &&
|
||||
badges_loc.current.qry_printed_status === 'all' &&
|
||||
!badges_loc.current.qry_affiliations &&
|
||||
@@ -135,6 +137,28 @@ let lq__event_badge_obj_li = $derived.by(() => {
|
||||
|
||||
// Standardized Reactive Search Pattern (Aether UI V3)
|
||||
// 1. Isolate dependencies into a stable derived object
|
||||
// Resolve per-tier search constraints from the event config (mirrored from mod_badges_json).
|
||||
// Tier order (highest wins): trusted_access → auth (public/authenticated) → anonymous.
|
||||
let effective_search_limits = $derived.by(() => {
|
||||
if ($ae_loc.trusted_access) {
|
||||
return {
|
||||
result_limit: badges_loc.current.trusted_search_result_limit,
|
||||
min_chars: badges_loc.current.trusted_search_min_chars
|
||||
};
|
||||
}
|
||||
if ($ae_loc.authenticated_access) {
|
||||
// public_access / authenticated — signed in but below trusted
|
||||
return {
|
||||
result_limit: badges_loc.current.auth_search_result_limit,
|
||||
min_chars: badges_loc.current.auth_search_min_chars
|
||||
};
|
||||
}
|
||||
return {
|
||||
result_limit: badges_loc.current.anon_search_result_limit,
|
||||
min_chars: badges_loc.current.anon_search_min_chars
|
||||
};
|
||||
});
|
||||
|
||||
let search_params = $derived({
|
||||
v: badges_loc.current.search_version,
|
||||
str: (badges_loc.current.fulltext_search_qry_str ?? '')
|
||||
@@ -145,7 +169,9 @@ let search_params = $derived({
|
||||
aff: (badges_loc.current.qry_affiliations ?? '').toLowerCase().trim(),
|
||||
sort: badges_loc.current.qry_sort_order,
|
||||
event_id: $events_slct?.event_id,
|
||||
remote_first: badges_loc.current.qry__remote_first
|
||||
remote_first: badges_loc.current.qry__remote_first,
|
||||
result_limit: effective_search_limits.result_limit,
|
||||
min_chars: effective_search_limits.min_chars
|
||||
});
|
||||
|
||||
// 2. Controlled effect for triggering searches
|
||||
@@ -188,6 +214,18 @@ async function handle_search_refresh(params: any) {
|
||||
const type_code = params.type;
|
||||
const printed_status = params.printed;
|
||||
const aff_str = params.aff;
|
||||
const result_limit = params.result_limit;
|
||||
const min_chars = params.min_chars;
|
||||
|
||||
// Defense-in-depth: enforce min_chars even if the search component lets one through.
|
||||
if (qry_str.length < min_chars) {
|
||||
untrack(() => {
|
||||
event_badge_id_li = [];
|
||||
$events_sess.badges.search_status = 'done';
|
||||
$events_sess.badges.search_complete = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. FAST PATH: Local IDB Search
|
||||
if (!remote_first) {
|
||||
@@ -283,6 +321,9 @@ async function handle_search_refresh(params: any) {
|
||||
}
|
||||
});
|
||||
|
||||
// Cap results to the effective per-tier limit.
|
||||
local_results = local_results.slice(0, result_limit);
|
||||
|
||||
const local_ids = local_results
|
||||
.map((b) => b.event_badge_id)
|
||||
.filter(Boolean);
|
||||
@@ -340,7 +381,7 @@ async function handle_search_refresh(params: any) {
|
||||
printed_status: printed_status,
|
||||
affiliations_qry_str: aff_str || null,
|
||||
order_by_li: order_by_li,
|
||||
limit: 150,
|
||||
limit: result_limit,
|
||||
log_lvl: 0
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user