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:
@@ -109,7 +109,13 @@ const cfg_defaults: BadgesRemoteCfg = {
|
||||
authenticated: { can_edit: [...default_authenticated_can_edit] },
|
||||
trusted: { can_edit: [...default_trusted_can_edit] },
|
||||
administrator: { can_edit: '*' }
|
||||
}
|
||||
},
|
||||
anon_search_result_limit: 15,
|
||||
anon_search_min_chars: 3,
|
||||
auth_search_result_limit: 25,
|
||||
auth_search_min_chars: 2,
|
||||
trusted_search_result_limit: 150,
|
||||
trusted_search_min_chars: 1
|
||||
};
|
||||
|
||||
let draft: BadgesRemoteCfg = $state({ ...cfg_defaults });
|
||||
@@ -230,6 +236,7 @@ async function save() {
|
||||
// Section collapse state
|
||||
let sections: Record<string, boolean> = $state({
|
||||
ui: true,
|
||||
search_limits: true,
|
||||
qr: true,
|
||||
passcodes: true,
|
||||
auth_fields: true,
|
||||
@@ -325,6 +332,95 @@ function toggle(key: string) {
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<!-- ================================================================ -->
|
||||
<!-- SEARCH LIMITS -->
|
||||
<!-- ================================================================ -->
|
||||
<section class="border-surface-200-800 rounded-xl border">
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full items-center justify-between px-4 py-3 text-left font-semibold"
|
||||
onclick={() => toggle('search_limits')}>
|
||||
<span>Search Limits per Access Tier</span>
|
||||
{#if sections.search_limits}<ChevronUp size="1em" />{:else}<ChevronDown size="1em" />{/if}
|
||||
</button>
|
||||
{#if sections.search_limits}
|
||||
<div class="border-surface-200-800 border-t px-4 py-3 space-y-4">
|
||||
<p class="text-xs text-surface-400">
|
||||
Controls how many results each access tier can see and how many characters
|
||||
they must type before a search fires. Trusted and above use the trusted limits.
|
||||
</p>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
||||
<!-- Anonymous -->
|
||||
<div class="space-y-2">
|
||||
<p class="text-sm font-semibold">Anonymous <span class="text-xs font-normal text-surface-400">(not signed in)</span></p>
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-surface-500">Result limit</span>
|
||||
<input
|
||||
type="number"
|
||||
class="input input-sm"
|
||||
min="1"
|
||||
max="500"
|
||||
bind:value={draft.anon_search_result_limit} />
|
||||
</label>
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-surface-500">Min characters to search</span>
|
||||
<input
|
||||
type="number"
|
||||
class="input input-sm"
|
||||
min="1"
|
||||
max="10"
|
||||
bind:value={draft.anon_search_min_chars} />
|
||||
</label>
|
||||
</div>
|
||||
<!-- Auth (Public / Authenticated) -->
|
||||
<div class="space-y-2">
|
||||
<p class="text-sm font-semibold">Auth <span class="text-xs font-normal text-surface-400">(public passcode / identity-verified)</span></p>
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-surface-500">Result limit</span>
|
||||
<input
|
||||
type="number"
|
||||
class="input input-sm"
|
||||
min="1"
|
||||
max="500"
|
||||
bind:value={draft.auth_search_result_limit} />
|
||||
</label>
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-surface-500">Min characters to search</span>
|
||||
<input
|
||||
type="number"
|
||||
class="input input-sm"
|
||||
min="1"
|
||||
max="10"
|
||||
bind:value={draft.auth_search_min_chars} />
|
||||
</label>
|
||||
</div>
|
||||
<!-- Trusted+ -->
|
||||
<div class="space-y-2">
|
||||
<p class="text-sm font-semibold">Trusted+ <span class="text-xs font-normal text-surface-400">(onsite staff and above)</span></p>
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-surface-500">Result limit</span>
|
||||
<input
|
||||
type="number"
|
||||
class="input input-sm"
|
||||
min="1"
|
||||
max="1000"
|
||||
bind:value={draft.trusted_search_result_limit} />
|
||||
</label>
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-surface-500">Min characters to search</span>
|
||||
<input
|
||||
type="number"
|
||||
class="input input-sm"
|
||||
min="1"
|
||||
max="10"
|
||||
bind:value={draft.trusted_search_min_chars} />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<!-- ================================================================ -->
|
||||
<!-- QR CONFIG -->
|
||||
<!-- ================================================================ -->
|
||||
|
||||
Reference in New Issue
Block a user