Update getContentItems to return total count and items

The changes involved modifying the implementation of `getContentItems` across multiple files, specifically in the CMS-related codebase. This method now returns an object containing the total count of items and the items themselves. The updates also included necessary adjustments in the code where `getContentItems` is called to accommodate the new structure of the returned result.
This commit is contained in:
giancarlo
2024-04-10 16:29:19 +08:00
parent 44373c0372
commit f94557e333
8 changed files with 89 additions and 71 deletions

View File

@@ -23,7 +23,7 @@ async function BlogPage({ searchParams }: { searchParams: { page: string } }) {
const limit = 10;
const offset = page * limit;
const posts = await cms.getContentItems({
const { items: posts, total } = await cms.getContentItems({
collection: 'posts',
limit,
offset,

View File

@@ -5,12 +5,10 @@ import { DocsNavigation } from '~/(marketing)/docs/_components/docs-navigation';
async function DocsLayout({ children }: React.PropsWithChildren) {
const cms = await createCmsClient();
const pages = await cms.getContentItems({
const { items: pages } = await cms.getContentItems({
collection: 'documentation',
});
console.log(pages);
return (
<div className={'flex'}>
<DocsNavigation pages={buildDocumentationTree(pages)} />

View File

@@ -18,12 +18,12 @@ async function DocsPage() {
const client = await createCmsClient();
const { t } = await createI18nServerInstance();
const docs = await client.getContentItems({
const { items } = await client.getContentItems({
collection: 'documentation',
});
// Filter out any docs that have a parentId, as these are children of other docs
const cards = docs.filter((item) => !item.parentId);
const cards = items.filter((item) => !item.parentId);
return (
<PageBody>
@@ -33,7 +33,7 @@ async function DocsPage() {
subtitle={t('marketing:documentationSubtitle')}
/>
<div className={'flex flex-col items-center'}>
<div className={'container mx-auto flex flex-col items-center'}>
<DocsCards cards={cards} />
</div>
</div>

View File

@@ -9,14 +9,14 @@ invariant(appConfig.url, 'No NEXT_PUBLIC_SITE_URL environment variable found');
export async function GET() {
const urls = getSiteUrls();
const client = await createCmsClient();
const contentItems = await client.getContentItems();
const items = await getAllItems();
return getServerSideSitemap([
...urls,
...contentItems.map((item) => {
...items.map((path) => {
return {
loc: new URL(item.url, appConfig.url).href,
loc: new URL(path, appConfig.url).href,
lastmod: new Date().toISOString(),
};
}),
@@ -33,3 +33,23 @@ function getSiteUrls() {
};
});
}
async function getAllItems() {
const client = await createCmsClient();
const posts = client
.getContentItems({
collection: 'posts',
})
.then((response) => response.items)
.then((posts) => posts.map((post) => `/blog/${post.slug}`));
const docs = client
.getContentItems({
collection: 'documentation',
})
.then((response) => response.items)
.then((docs) => docs.map((doc) => `/docs/${doc.slug}`));
return Promise.all([posts, docs]).then((items) => items.flat());
}

View File

@@ -59,9 +59,10 @@ export abstract class CmsClient {
* @param options - Options for filtering and pagination.
* @returns A promise that resolves to an array of content items.
*/
abstract getContentItems(
options?: Cms.GetContentItemsOptions,
): Promise<Cms.ContentItem[]>;
abstract getContentItems(options?: Cms.GetContentItemsOptions): Promise<{
total: number;
items: Cms.ContentItem[];
}>;
/**
* Retrieves a content item by its ID and type.

View File

@@ -22,38 +22,40 @@ export class KeystaticClient implements CmsClient {
const startOffset = options?.offset ?? 0;
const endOffset = startOffset + (options?.limit ?? 10);
return Promise.all(
docs
.filter((item) => {
const categoryMatch = options?.categories
? options.categories.find((category) =>
item.entry.categories.includes(category),
)
: true;
const filtered = docs.filter((item) => {
const categoryMatch = options?.categories
? options.categories.find((category) =>
item.entry.categories.includes(category),
)
: true;
if (!categoryMatch) {
return false;
}
if (!categoryMatch) {
return false;
}
const tagMatch = options?.tags
? options.tags.find((tag) => item.entry.tags.includes(tag))
: true;
const tagMatch = options?.tags
? options.tags.find((tag) => item.entry.tags.includes(tag))
: true;
if (!tagMatch) {
return false;
}
if (!tagMatch) {
return false;
}
return true;
})
.slice(startOffset, endOffset)
.map(async (item) => {
const children = docs.filter(
(item) => item.entry.parent === item.slug,
);
return true;
});
return this.mapPost(item, children);
}),
const items = await Promise.all(
filtered.slice(startOffset, endOffset).map(async (item) => {
const children = docs.filter((item) => item.entry.parent === item.slug);
return this.mapPost(item, children);
}),
);
return {
total: filtered.length,
items,
};
}
async getContentItemBySlug(params: { slug: string; collection: string }) {

View File

@@ -79,12 +79,14 @@ export class WordpressClient implements CmsClient {
const url = `${this.apiUrl}${endpoint}`;
const posts = await fetch(url).then(
(value) => value.json() as Promise<WP_REST_API_Post[]>,
);
const response = await fetch(url);
const totalHeader = response.headers.get('X-WP-Total');
return Promise.all(
posts.map(async (item: WP_REST_API_Post) => {
const total = totalHeader ? Number(totalHeader) : 0;
const results = (await response.json()) as WP_REST_API_Post[];
const posts = await Promise.all(
results.map(async (item: WP_REST_API_Post) => {
let parentId: string | undefined;
if (!item) {
@@ -117,6 +119,11 @@ export class WordpressClient implements CmsClient {
};
}),
);
return {
total,
items: posts,
};
}
async getContentItemBySlug({

View File

@@ -173,6 +173,7 @@ create policy "public config can be read by authenticated users" on
for select to authenticated
using (true);
-- Function to get the config settings
create or replace function public.get_config()
returns json
as $$
@@ -215,8 +216,7 @@ end
$$
language plpgsql;
-- Automatically set user tracking on tables when a row is inserted or
-- updated
-- Automatically set user tracking on tables when a row is inserted or updated
create or replace function public.trigger_set_user_tracking()
returns trigger
as $$
@@ -240,6 +240,7 @@ language plpgsql;
grant execute on function public.get_config() to authenticated, service_role;
-- check if a field is set in the config
create or replace function public.is_set(field_name text)
returns boolean
as $$
@@ -278,6 +279,7 @@ create table if not exists public.accounts(
created_by uuid references auth.users,
updated_by uuid references auth.users,
picture_url varchar(1000),
public_data jsonb default '{}'::jsonb not null,
primary key (id)
);
@@ -1733,6 +1735,7 @@ public.is_set(
'enable_team_accounts')
and public.accounts.is_personal_account = false);
-- Function: create an invitation to an account
create or replace function public.create_invitation(account_id uuid,
email text, role varchar(50))
returns public.invitations
@@ -1765,30 +1768,10 @@ end;
$$
language plpgsql;
create or replace function public.get_user_accounts()
returns setof public.accounts
as $$
begin
select
id,
name,
picture_url
from
public.accounts
join public.accounts_memberships on accounts.id =
accounts_memberships.account_id
where
accounts_memberships.user_id = auth.uid();
end;
$$
language plpgsql;
--
-- VIEW "user_account_workspace":
-- we create a view to load the general app data for the authenticated
-- user
-- which includes the user's accounts, memberships, and roles, and
-- relative subscription status
-- user which includes the user's accounts, memberships, and roles, and relative subscription status
create or replace view public.user_account_workspace as
select
accounts.id as id,
@@ -1804,6 +1787,10 @@ where
grant select on public.user_account_workspace to authenticated, service_role;
--
-- VIEW "user_accounts":
-- we create a view to load the user's accounts and memberships
-- useful to display the user's accounts in the app
create or replace view public.user_accounts as
select
accounts.id as id,
@@ -1821,6 +1808,9 @@ where
grant select on public.user_accounts to authenticated, service_role;
--
-- Function: get the account workspace for an organization account
-- to load all the required data for the authenticated user within the account scope
create or replace function
public.organization_account_workspace(account_slug text)
returns table(
@@ -1872,6 +1862,7 @@ language plpgsql;
grant execute on function public.organization_account_workspace(text)
to authenticated, service_role;
-- Functions: get account members
create or replace function public.get_account_members(account_slug text)
returns table(
id uuid,
@@ -1961,7 +1952,6 @@ create type kit.invitation as (
email text,
role varchar( 50));
-- Then, modify your function to use this type
create or replace function
public.add_invitations_to_account(account_slug text, invitations
kit.invitation[])