feat(frontend): add search to flags page, add pagination to profile flags page

This commit is contained in:
sam 2025-04-11 16:31:18 +02:00
parent 35c5b520db
commit 1adb26e8b8
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
3 changed files with 85 additions and 24 deletions

View file

@ -5,8 +5,11 @@
currentPage: number; currentPage: number;
pageCount: number; pageCount: number;
center?: boolean; center?: boolean;
listAllPages?: boolean;
}; };
let { currentPage = $bindable(), pageCount, center }: Props = $props(); let { currentPage = $bindable(), pageCount, center, listAllPages }: Props = $props();
let allPages = $derived(listAllPages === undefined || listAllPages);
let prevPage = $derived(currentPage > 0 ? currentPage - 1 : 0); let prevPage = $derived(currentPage > 0 ? currentPage - 1 : 0);
let nextPage = $derived(currentPage < pageCount - 1 ? currentPage + 1 : pageCount - 1); let nextPage = $derived(currentPage < pageCount - 1 ? currentPage + 1 : pageCount - 1);
@ -21,11 +24,27 @@
<PaginationItem> <PaginationItem>
<PaginationLink previous onclick={() => (currentPage = prevPage)} /> <PaginationLink previous onclick={() => (currentPage = prevPage)} />
</PaginationItem> </PaginationItem>
{#each new Array(pageCount) as _, page} {#if allPages}
<PaginationItem active={page === currentPage}> {#each new Array(pageCount) as _, page}
<PaginationLink onclick={() => (currentPage = page)}>{page + 1}</PaginationLink> <PaginationItem active={page === currentPage}>
<PaginationLink onclick={() => (currentPage = page)}>{page + 1}</PaginationLink>
</PaginationItem>
{/each}
{:else}
{#if currentPage !== 0}
<PaginationItem onclick={() => (currentPage = prevPage)}>
<PaginationLink>{currentPage}</PaginationLink>
</PaginationItem>
{/if}
<PaginationItem active>
<PaginationLink>{currentPage + 1}</PaginationLink>
</PaginationItem> </PaginationItem>
{/each} {#if currentPage !== pageCount - 1}
<PaginationItem onclick={() => (currentPage = nextPage)}>
<PaginationLink>{currentPage + 2}</PaginationLink>
</PaginationItem>
{/if}
{/if}
<PaginationItem> <PaginationItem>
<PaginationLink next onclick={() => (currentPage = nextPage)} /> <PaginationLink next onclick={() => (currentPage = nextPage)} />
</PaginationItem> </PaginationItem>

View file

@ -4,23 +4,35 @@
import Search from "svelte-bootstrap-icons/lib/Search.svelte"; import Search from "svelte-bootstrap-icons/lib/Search.svelte";
import FlagButton from "./FlagButton.svelte"; import FlagButton from "./FlagButton.svelte";
import { t } from "$lib/i18n"; import { t } from "$lib/i18n";
import paginate from "$lib/paginate";
import ClientPaginator from "$components/ClientPaginator.svelte";
type Props = { flags: PrideFlag[]; select(flag: PrideFlag): void }; type Props = { flags: PrideFlag[]; select(flag: PrideFlag): void };
let { flags, select }: Props = $props(); let { flags, select }: Props = $props();
const FLAGS_PER_PAGE = 5;
let query = $state(""); let query = $state("");
let filteredFlags = $derived(search(query)); let filteredFlags = $derived(search(query));
let arr: PrideFlag[] = $state([]);
let currentPage = $state(0);
let pageCount = $state(0);
$effect(() => {
const pages = paginate(filteredFlags, currentPage, FLAGS_PER_PAGE);
arr = pages.data;
pageCount = pages.pageCount;
});
function search(q: string) { function search(q: string) {
if (!q) return flags.slice(0, 20); return flags.filter((f) => f.name.toLowerCase().indexOf(q.toLowerCase()) !== -1);
return flags.filter((f) => f.name.toLowerCase().indexOf(q.toLowerCase()) !== -1).slice(0, 20);
} }
</script> </script>
<input class="form-control" placeholder={$t("editor.flag-search-placeholder")} bind:value={query} /> <input class="form-control" placeholder={$t("editor.flag-search-placeholder")} bind:value={query} />
<div class="mt-3"> <div class="mt-3">
{#each filteredFlags as flag (flag.id)} {#each arr as flag (flag.id)}
<FlagButton {flag} onclick={() => select(flag)} padding /> <FlagButton {flag} onclick={() => select(flag)} padding />
{:else} {:else}
<div class="text-secondary text-center"> <div class="text-secondary text-center">
@ -36,6 +48,7 @@
</p> </p>
</div> </div>
{/each} {/each}
<ClientPaginator bind:currentPage {pageCount} listAllPages={false} />
{#if flags.length > 0} {#if flags.length > 0}
<p class="text-secondary mt-2"> <p class="text-secondary mt-2">
<InfoCircleFill aria-hidden /> <InfoCircleFill aria-hidden />

View file

@ -13,6 +13,7 @@
import ApiError from "$api/error"; import ApiError from "$api/error";
import log from "$lib/log"; import log from "$lib/log";
import FormStatusMarker from "$components/editor/FormStatusMarker.svelte"; import FormStatusMarker from "$components/editor/FormStatusMarker.svelte";
import Search from "svelte-bootstrap-icons/lib/Search.svelte";
type Props = { data: PageData; form: ActionData }; type Props = { data: PageData; form: ActionData };
let { data, form }: Props = $props(); let { data, form }: Props = $props();
@ -26,13 +27,17 @@
const FLAGS_PER_PAGE = 50; const FLAGS_PER_PAGE = 50;
$effect(() => { $effect(() => {
const pages = paginate(flags, currentPage, FLAGS_PER_PAGE); const filteredFlags = search
? flags.filter((f) => f.name.toLocaleLowerCase().indexOf(search.toLocaleLowerCase()) !== -1)
: flags;
const pages = paginate(filteredFlags, currentPage, FLAGS_PER_PAGE);
arr = pages.data; arr = pages.data;
pageCount = pages.pageCount; pageCount = pages.pageCount;
}); });
let lastEditedFlag: string | null = $state(null); let lastEditedFlag: string | null = $state(null);
let ok: { ok: boolean; error: RawApiError | null } | null = $state(null); let ok: { ok: boolean; error: RawApiError | null } | null = $state(null);
let search: string = $state("");
const update = async ( const update = async (
id: string, id: string,
@ -109,20 +114,44 @@
})} })}
</h4> </h4>
<ClientPaginator center bind:currentPage {pageCount} /> <div class="mb-3">
<input
<Accordion class="mb-3"> class="form-control"
{#each arr as flag (flag.id)} type="text"
<AccordionItem> bind:value={search}
<span slot="header"> placeholder={$t("editor.flag-search-placeholder")}
<EditorFlagImage {flag} /> />
{flag.name} </div>
</span>
<ClientPaginator center bind:currentPage {pageCount} />
{#if lastEditedFlag === flag.id}<FormStatusMarker form={ok} />{/if}
<FlagEditor {flag} {update} {deleteFlag} /> {#if arr.length === 0}
</AccordionItem> <div class="text-secondary text-center">
{/each} <p>
</Accordion> <Search class="no-flags-icon" height={64} width={64} aria-hidden />
</p>
<p>
{#if search}
{$t("editor.flag-search-no-flags")}
{:else}
{$t("editor.flag-search-no-account-flags")}
{/if}
</p>
</div>
{:else}
<Accordion class="mb-3">
{#each arr as flag (flag.id)}
<AccordionItem>
<span slot="header">
<EditorFlagImage {flag} />
{flag.name}
</span>
{#if lastEditedFlag === flag.id}<FormStatusMarker form={ok} />{/if}
<FlagEditor {flag} {update} {deleteFlag} />
</AccordionItem>
{/each}
</Accordion>
{/if}
<ClientPaginator center bind:currentPage {pageCount} /> <ClientPaginator center bind:currentPage {pageCount} />