pocketbase/ui/src/components/records/PageRecords.svelte

279 lines
9.1 KiB
Svelte
Raw Normal View History

2022-07-07 05:19:05 +08:00
<script>
import { tick } from "svelte";
import { querystring } from "svelte-spa-router";
2023-02-19 01:33:42 +08:00
import CommonHelper from "@/utils/CommonHelper";
2022-07-07 05:19:05 +08:00
import {
collections,
activeCollection,
isCollectionsLoading,
loadCollections,
2022-10-30 16:28:14 +08:00
changeActiveCollectionById,
2022-07-07 05:19:05 +08:00
} from "@/stores/collections";
import tooltip from "@/actions/tooltip";
2022-08-18 22:10:42 +08:00
import { pageTitle, hideControls } from "@/stores/app";
2022-08-09 21:16:09 +08:00
import PageWrapper from "@/components/base/PageWrapper.svelte";
2022-07-07 05:19:05 +08:00
import Searchbar from "@/components/base/Searchbar.svelte";
import RefreshButton from "@/components/base/RefreshButton.svelte";
2022-07-07 05:19:05 +08:00
import CollectionsSidebar from "@/components/collections/CollectionsSidebar.svelte";
import CollectionUpsertPanel from "@/components/collections/CollectionUpsertPanel.svelte";
2022-10-30 16:28:14 +08:00
import CollectionDocsPanel from "@/components/collections/CollectionDocsPanel.svelte";
2022-07-07 05:19:05 +08:00
import RecordUpsertPanel from "@/components/records/RecordUpsertPanel.svelte";
2023-02-19 01:33:42 +08:00
import RecordPreviewPanel from "@/components/records/RecordPreviewPanel.svelte";
2022-07-07 05:19:05 +08:00
import RecordsList from "@/components/records/RecordsList.svelte";
import RecordsCount from "@/components/records/RecordsCount.svelte";
2022-07-07 05:19:05 +08:00
const initialQueryParams = new URLSearchParams($querystring);
2022-08-01 19:20:21 +08:00
2022-07-07 05:19:05 +08:00
let collectionUpsertPanel;
let collectionDocsPanel;
2023-02-19 01:33:42 +08:00
let recordUpsertPanel;
let recordPreviewPanel;
2022-07-07 05:19:05 +08:00
let recordsList;
let recordsCount;
let filter = initialQueryParams.get("filter") || "";
let sort = initialQueryParams.get("sort") || "-created";
let selectedCollectionId = initialQueryParams.get("collectionId") || $activeCollection?.id;
let totalCount = 0; // used to manully change the count without the need of reloading the recordsCount component
2022-07-07 05:19:05 +08:00
loadCollections(selectedCollectionId);
2022-10-30 16:28:14 +08:00
$: reactiveParams = new URLSearchParams($querystring);
$: if (
!$isCollectionsLoading &&
reactiveParams.get("collectionId") &&
2022-10-30 16:28:14 +08:00
reactiveParams.get("collectionId") != selectedCollectionId
) {
changeActiveCollectionById(reactiveParams.get("collectionId"));
}
2022-07-07 05:19:05 +08:00
// reset filter and sort on collection change
$: if ($activeCollection?.id && selectedCollectionId != $activeCollection.id) {
reset();
2022-07-07 05:19:05 +08:00
}
$: if ($activeCollection?.id) {
normalizeSort();
}
$: if (!$isCollectionsLoading && initialQueryParams.get("recordId")) {
showRecordById(initialQueryParams.get("recordId"));
}
2022-07-07 05:19:05 +08:00
// keep the url params in sync
$: if (!$isCollectionsLoading && (sort || filter || $activeCollection?.id)) {
updateQueryParams();
2022-07-07 05:19:05 +08:00
}
$: $pageTitle = $activeCollection?.name || "Collections";
async function showRecordById(recordId) {
await tick(); // ensure that the reactive component params are resolved
$activeCollection?.type === "view"
? recordPreviewPanel.show(recordId)
: recordUpsertPanel?.show(recordId);
}
function reset() {
2023-02-19 01:33:42 +08:00
selectedCollectionId = $activeCollection?.id;
filter = "";
2023-02-19 01:33:42 +08:00
sort = "-created";
updateQueryParams({ recordId: null });
normalizeSort();
}
// ensures that the sort fields exist in the collection
async function normalizeSort() {
if (!sort) {
return; // nothing to normalize
}
const collectionFields = CommonHelper.getAllCollectionIdentifiers($activeCollection);
const sortFields = sort.split(",").map((f) => {
if (f.startsWith("+") || f.startsWith("-")) {
return f.substring(1);
}
return f;
});
// invalid sort expression or missing sort field
if (sortFields.filter((f) => collectionFields.includes(f)).length != sortFields.length) {
if (collectionFields.includes("created")) {
sort = "-created";
} else {
sort = "";
}
2023-02-19 01:33:42 +08:00
}
}
2022-07-07 05:19:05 +08:00
function updateQueryParams(extra = {}) {
const query = Object.assign(
{
collectionId: $activeCollection?.id || "",
filter: filter,
sort: sort,
},
extra
);
CommonHelper.replaceQueryParams(query);
}
2022-07-07 05:19:05 +08:00
</script>
{#if $isCollectionsLoading && !$collections.length}
2022-08-09 21:16:09 +08:00
<PageWrapper center>
<div class="placeholder-section m-b-base">
<span class="loader loader-lg" />
<h1>Loading collections...</h1>
</div>
</PageWrapper>
2022-10-30 16:28:14 +08:00
{:else if !$collections.length}
2022-08-09 21:16:09 +08:00
<PageWrapper center>
<div class="placeholder-section m-b-base">
<div class="icon">
<i class="ri-database-2-line" />
</div>
2022-08-18 22:10:42 +08:00
{#if $hideControls}
<h1 class="m-b-10">You don't have any collections yet.</h1>
{:else}
<h1 class="m-b-10">Create your first collection to add records!</h1>
<button
type="button"
class="btn btn-expanded-lg btn-lg"
on:click={() => collectionUpsertPanel?.show()}
>
<i class="ri-add-line" />
<span class="txt">Create new collection</span>
</button>
{/if}
2022-07-07 05:19:05 +08:00
</div>
2022-08-09 21:16:09 +08:00
</PageWrapper>
2022-07-07 05:19:05 +08:00
{:else}
<CollectionsSidebar />
<PageWrapper class="flex-content">
2022-07-07 05:19:05 +08:00
<header class="page-header">
<nav class="breadcrumbs">
<div class="breadcrumb-item">Collections</div>
<div class="breadcrumb-item">{$activeCollection.name}</div>
</nav>
2022-08-05 11:00:38 +08:00
<div class="inline-flex gap-5">
2022-08-18 22:10:42 +08:00
{#if !$hideControls}
<button
type="button"
aria-label="Edit collection"
class="btn btn-transparent btn-circle"
2022-08-18 22:10:42 +08:00
use:tooltip={{ text: "Edit collection", position: "right" }}
on:click={() => collectionUpsertPanel?.show($activeCollection)}
>
<i class="ri-settings-4-line" />
</button>
{/if}
2022-08-05 11:00:38 +08:00
<RefreshButton on:refresh={() => recordsList?.load()} />
</div>
2022-07-07 05:19:05 +08:00
<div class="btns-group">
<button
type="button"
class="btn btn-outline"
on:click={() => collectionDocsPanel?.show($activeCollection)}
>
<i class="ri-code-s-slash-line" />
<span class="txt">API Preview</span>
</button>
2023-08-15 02:20:49 +08:00
{#if $activeCollection.type !== "view"}
2023-02-19 01:33:42 +08:00
<button type="button" class="btn btn-expanded" on:click={() => recordUpsertPanel?.show()}>
<i class="ri-add-line" />
<span class="txt">New record</span>
</button>
{/if}
2022-07-07 05:19:05 +08:00
</div>
</header>
<Searchbar
value={filter}
autocompleteCollection={$activeCollection}
on:submit={(e) => (filter = e.detail)}
/>
<div class="clearfix m-b-sm" />
2022-07-07 05:19:05 +08:00
<RecordsList
bind:this={recordsList}
collection={$activeCollection}
bind:filter
bind:sort
2023-02-19 01:33:42 +08:00
on:select={(e) => {
updateQueryParams({
recordId: e.detail.id,
});
let showModel = e.detail._partial ? e.detail.id : e.detail;
2023-08-15 02:20:49 +08:00
$activeCollection.type === "view"
? recordPreviewPanel?.show(showModel)
: recordUpsertPanel?.show(showModel);
2023-02-19 01:33:42 +08:00
}}
on:delete={() => {
recordsCount?.reload();
}}
2023-02-19 01:33:42 +08:00
on:new={() => recordUpsertPanel?.show()}
2022-07-07 05:19:05 +08:00
/>
<svelte:fragment slot="footer">
<RecordsCount
bind:this={recordsCount}
class="m-r-auto txt-sm txt-hint"
collection={$activeCollection}
{filter}
bind:totalCount
/>
</svelte:fragment>
2022-08-09 21:16:09 +08:00
</PageWrapper>
2022-07-07 05:19:05 +08:00
{/if}
<CollectionUpsertPanel bind:this={collectionUpsertPanel} />
2022-07-07 05:19:05 +08:00
<CollectionDocsPanel bind:this={collectionDocsPanel} />
<RecordUpsertPanel
2023-02-19 01:33:42 +08:00
bind:this={recordUpsertPanel}
2022-07-07 05:19:05 +08:00
collection={$activeCollection}
on:hide={() => {
updateQueryParams({ recordId: null });
}}
on:save={(e) => {
if (filter) {
// if there is applied filter, reload the count since we
// don't know after the save whether the record satisfies it
recordsCount?.reload();
} else if (e.detail.isNew) {
totalCount++;
}
recordsList?.reloadLoadedPages();
}}
on:delete={(e) => {
if (!filter || recordsList?.hasRecord(e.detail.id)) {
totalCount--;
}
recordsList?.reloadLoadedPages();
}}
2022-07-07 05:19:05 +08:00
/>
2023-02-19 01:33:42 +08:00
<RecordPreviewPanel
bind:this={recordPreviewPanel}
collection={$activeCollection}
on:hide={() => {
updateQueryParams({ recordId: null });
}}
/>