mirror of
https://github.com/thiloho/archtika.git
synced 2025-11-22 10:51:36 +01:00
Create pagination component and paginate manage page as well
This commit is contained in:
82
web-app/src/lib/components/Pagination.svelte
Normal file
82
web-app/src/lib/components/Pagination.svelte
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { page } from "$app/stores";
|
||||||
|
import { PAGINATION_MAX_ITEMS } from "$lib/utils";
|
||||||
|
|
||||||
|
const { commonFilters = [], resultCount }: { commonFilters?: string[]; resultCount: number } =
|
||||||
|
$props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
{#snippet commonFilterInputs()}
|
||||||
|
{#each commonFilters as filter}
|
||||||
|
<input type="hidden" name={filter} value={$page.url.searchParams.get(filter)} />
|
||||||
|
{/each}
|
||||||
|
{/snippet}
|
||||||
|
<p>
|
||||||
|
{$page.url.searchParams.get("page") ?? 1} / {Math.max(
|
||||||
|
Math.ceil(resultCount / PAGINATION_MAX_ITEMS),
|
||||||
|
1
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<form method="GET">
|
||||||
|
<input type="hidden" name="page" value={1} />
|
||||||
|
{@render commonFilterInputs()}
|
||||||
|
<button type="submit" disabled={($page.url.searchParams.get("page") ?? "1") === "1"}
|
||||||
|
>First</button
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
<form method="GET">
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="page"
|
||||||
|
value={Math.max(1, Number.parseInt($page.url.searchParams.get("page") ?? "1") - 1)}
|
||||||
|
/>
|
||||||
|
{@render commonFilterInputs()}
|
||||||
|
<button type="submit" disabled={($page.url.searchParams.get("page") ?? "1") === "1"}
|
||||||
|
>Previous</button
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
<form method="GET">
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="page"
|
||||||
|
value={Math.min(
|
||||||
|
Math.max(Math.ceil(resultCount / PAGINATION_MAX_ITEMS), 1),
|
||||||
|
Number.parseInt($page.url.searchParams.get("page") ?? "1") + 1
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{@render commonFilterInputs()}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={($page.url.searchParams.get("page") ?? "1") ===
|
||||||
|
Math.max(Math.ceil(resultCount / PAGINATION_MAX_ITEMS), 1).toString()}>Next</button
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
<form method="GET">
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="page"
|
||||||
|
value={Math.max(Math.ceil(resultCount / PAGINATION_MAX_ITEMS), 1)}
|
||||||
|
/>
|
||||||
|
{@render commonFilterInputs()}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={($page.url.searchParams.get("page") ?? "1") ===
|
||||||
|
Math.max(Math.ceil(resultCount / PAGINATION_MAX_ITEMS), 1).toString()}>Last</button
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination > form:first-of-type {
|
||||||
|
margin-inline-start: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -179,6 +179,8 @@ export const enhanceForm = (options?: {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PAGINATION_MAX_ITEMS = 20;
|
||||||
|
|
||||||
export const hexToHSL = (hex: string) => {
|
export const hexToHSL = (hex: string) => {
|
||||||
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
||||||
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import type { Actions, PageServerLoad } from "./$types";
|
import type { Actions, PageServerLoad } from "./$types";
|
||||||
import { API_BASE_PREFIX } from "$lib/server/utils";
|
import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
|
||||||
import { apiRequest } from "$lib/server/utils";
|
|
||||||
import type { Website, User } from "$lib/db-schema";
|
import type { Website, User } from "$lib/db-schema";
|
||||||
|
import { PAGINATION_MAX_ITEMS } from "$lib/utils";
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ fetch, url }) => {
|
||||||
|
const currentPage = Number.parseInt(url.searchParams.get("page") ?? "1");
|
||||||
|
const resultOffset = (currentPage - 1) * PAGINATION_MAX_ITEMS;
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ fetch }) => {
|
|
||||||
const usersWithWebsites: (User & { website: Website[] })[] = (
|
const usersWithWebsites: (User & { website: Website[] })[] = (
|
||||||
await apiRequest(
|
await apiRequest(
|
||||||
fetch,
|
fetch,
|
||||||
`${API_BASE_PREFIX}/user?select=*,website!user_id(*)&order=created_at`,
|
`${API_BASE_PREFIX}/user?select=*,website!user_id(*)&order=created_at&limit=${PAGINATION_MAX_ITEMS}&offset=${resultOffset}`,
|
||||||
"GET",
|
"GET",
|
||||||
{
|
{
|
||||||
returnData: true
|
returnData: true
|
||||||
@@ -15,9 +18,19 @@ export const load: PageServerLoad = async ({ fetch }) => {
|
|||||||
)
|
)
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
|
const resultUsers = await apiRequest(fetch, `${API_BASE_PREFIX}/user`, "HEAD", {
|
||||||
|
headers: {
|
||||||
|
Prefer: "count=exact"
|
||||||
|
},
|
||||||
|
returnData: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const resultUsersCount = Number(resultUsers.data.headers.get("content-range")?.split("/").at(-1));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
usersWithWebsites,
|
usersWithWebsites,
|
||||||
API_BASE_PREFIX
|
API_BASE_PREFIX,
|
||||||
|
resultUsersCount
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import { enhanceForm } from "$lib/utils";
|
import { enhanceForm } from "$lib/utils";
|
||||||
import { sending } from "$lib/runes.svelte";
|
import { sending } from "$lib/runes.svelte";
|
||||||
import DateTime from "$lib/components/DateTime.svelte";
|
import DateTime from "$lib/components/DateTime.svelte";
|
||||||
|
import Pagination from "$lib/components/Pagination.svelte";
|
||||||
|
|
||||||
const { data, form }: { data: PageServerData; form: ActionData } = $props();
|
const { data, form }: { data: PageServerData; form: ActionData } = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -18,9 +19,15 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<section id="all-users">
|
<section id="all-users">
|
||||||
|
<hgroup>
|
||||||
<h2>
|
<h2>
|
||||||
<a href="#all-users">All users</a>
|
<a href="#all-users">All users</a>
|
||||||
</h2>
|
</h2>
|
||||||
|
<p>
|
||||||
|
<strong>{data.resultUsersCount.toLocaleString("en", { useGrouping: true })}</strong>
|
||||||
|
<small>result(s)</small>
|
||||||
|
</p>
|
||||||
|
</hgroup>
|
||||||
<div class="scroll-container">
|
<div class="scroll-container">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -117,6 +124,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<Pagination resultCount={data.resultUsersCount} />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import type { PageServerLoad, Actions } from "./$types";
|
|||||||
import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
|
import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
|
||||||
import type { ChangeLog, User, Collab } from "$lib/db-schema";
|
import type { ChangeLog, User, Collab } from "$lib/db-schema";
|
||||||
import DiffMatchPatch from "diff-match-patch";
|
import DiffMatchPatch from "diff-match-patch";
|
||||||
|
import { PAGINATION_MAX_ITEMS } from "$lib/utils";
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ parent, fetch, params, url }) => {
|
export const load: PageServerLoad = async ({ parent, fetch, params, url }) => {
|
||||||
const userFilter = url.searchParams.get("user");
|
const userFilter = url.searchParams.get("user");
|
||||||
const resourceFilter = url.searchParams.get("resource");
|
const resourceFilter = url.searchParams.get("resource");
|
||||||
const operationFilter = url.searchParams.get("operation");
|
const operationFilter = url.searchParams.get("operation");
|
||||||
const currentPage = Number.parseInt(url.searchParams.get("page") ?? "1");
|
const currentPage = Number.parseInt(url.searchParams.get("page") ?? "1");
|
||||||
const resultOffset = (currentPage - 1) * 20;
|
const resultOffset = (currentPage - 1) * PAGINATION_MAX_ITEMS;
|
||||||
|
|
||||||
const searchParams = new URLSearchParams();
|
const searchParams = new URLSearchParams();
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ export const load: PageServerLoad = async ({ parent, fetch, params, url }) => {
|
|||||||
searchParams.append("operation", `eq.${operationFilter.toUpperCase()}`);
|
searchParams.append("operation", `eq.${operationFilter.toUpperCase()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const constructedFetchUrl = `${baseFetchUrl}&${searchParams.toString()}&limit=20&offset=${resultOffset}`;
|
const constructedFetchUrl = `${baseFetchUrl}&${searchParams.toString()}&limit=${PAGINATION_MAX_ITEMS}&offset=${resultOffset}`;
|
||||||
|
|
||||||
const changeLog: (ChangeLog & { user: { username: User["username"] } })[] = (
|
const changeLog: (ChangeLog & { user: { username: User["username"] } })[] = (
|
||||||
await apiRequest(fetch, constructedFetchUrl, "GET", {
|
await apiRequest(fetch, constructedFetchUrl, "GET", {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
import { enhance } from "$app/forms";
|
import { enhance } from "$app/forms";
|
||||||
import { sending } from "$lib/runes.svelte";
|
import { sending } from "$lib/runes.svelte";
|
||||||
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
|
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
|
||||||
|
import Pagination from "$lib/components/Pagination.svelte";
|
||||||
|
|
||||||
const { data, form }: { data: PageServerData; form: ActionData } = $props();
|
const { data, form }: { data: PageServerData; form: ActionData } = $props();
|
||||||
|
|
||||||
@@ -162,79 +163,9 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="pagination">
|
<Pagination
|
||||||
{#snippet commonFilterInputs()}
|
commonFilters={["user", "resource", "operation"]}
|
||||||
<input type="hidden" name="user" value={$page.url.searchParams.get("user")} />
|
resultCount={data.resultChangeLogCount}
|
||||||
<input type="hidden" name="resource" value={$page.url.searchParams.get("resource")} />
|
|
||||||
<input type="hidden" name="operation" value={$page.url.searchParams.get("operation")} />
|
|
||||||
{/snippet}
|
|
||||||
<p>
|
|
||||||
{$page.url.searchParams.get("page") ?? 1} / {Math.max(
|
|
||||||
Math.ceil(data.resultChangeLogCount / 20),
|
|
||||||
1
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<form method="GET">
|
|
||||||
<input type="hidden" name="page" value={1} />
|
|
||||||
{@render commonFilterInputs()}
|
|
||||||
<button type="submit" disabled={($page.url.searchParams.get("page") ?? "1") === "1"}
|
|
||||||
>First</button
|
|
||||||
>
|
|
||||||
</form>
|
|
||||||
<form method="GET">
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
name="page"
|
|
||||||
value={Math.max(1, Number.parseInt($page.url.searchParams.get("page") ?? "1") - 1)}
|
|
||||||
/>
|
/>
|
||||||
{@render commonFilterInputs()}
|
|
||||||
<button type="submit" disabled={($page.url.searchParams.get("page") ?? "1") === "1"}
|
|
||||||
>Previous</button
|
|
||||||
>
|
|
||||||
</form>
|
|
||||||
<form method="GET">
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
name="page"
|
|
||||||
value={Math.min(
|
|
||||||
Math.max(Math.ceil(data.resultChangeLogCount / 20), 1),
|
|
||||||
Number.parseInt($page.url.searchParams.get("page") ?? "1") + 1
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{@render commonFilterInputs()}
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={($page.url.searchParams.get("page") ?? "1") ===
|
|
||||||
Math.max(Math.ceil(data.resultChangeLogCount / 20), 1).toString()}>Next</button
|
|
||||||
>
|
|
||||||
</form>
|
|
||||||
<form method="GET">
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
name="page"
|
|
||||||
value={Math.max(Math.ceil(data.resultChangeLogCount / 20), 1)}
|
|
||||||
/>
|
|
||||||
{@render commonFilterInputs()}
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={($page.url.searchParams.get("page") ?? "1") ===
|
|
||||||
Math.max(Math.ceil(data.resultChangeLogCount / 20), 1).toString()}>Last</button
|
|
||||||
>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</WebsiteEditor>
|
</WebsiteEditor>
|
||||||
|
|
||||||
<style>
|
|
||||||
.pagination {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: var(--space-xs);
|
|
||||||
justify-content: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination > form:first-of-type {
|
|
||||||
margin-inline-start: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user