Skip to content

Commit

Permalink
Adds project browser page
Browse files Browse the repository at this point in the history
  • Loading branch information
allanlasser committed Apr 19, 2024
1 parent 89b1b81 commit 2ded65e
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 3 deletions.
24 changes: 22 additions & 2 deletions src/lib/api/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Project, ProjectResults, ProjectMembershipList } from "./types";

import { error, type NumericRange } from "@sveltejs/kit";
import { BASE_API_URL } from "@/config/config.js";
import { isErrorCode } from "$lib/utils/api";
import { getAll, isErrorCode } from "$lib/utils/api";

/**
* Get a single project
Expand Down Expand Up @@ -33,7 +33,7 @@ export async function get(
}

/**
* Get a list of projects
* Get a page of projects
*
* @export
* @param {any} params filter params
Expand Down Expand Up @@ -62,6 +62,26 @@ export async function list(
return res.json();
}

/**
* Get a list of all projects owned by the user
*/
export async function getOwned(userId: number): Promise<Project[]> {
const endpoint = new URL("projects/", BASE_API_URL);
endpoint.searchParams.set("user", String(userId));
const projects = await getAll<Project>(endpoint);
return projects.filter((project) => project.user === userId);
}

/**
* Get a list of all projects shared with the user
*/
export async function getShared(userId: number): Promise<Project[]> {
const endpoint = new URL("projects/", BASE_API_URL);
endpoint.searchParams.set("user", String(userId));
const projects = await getAll<Project>(endpoint);
return projects.filter((project) => project.user !== userId);
}

/**
* Get documents in a project with membership access
*
Expand Down
23 changes: 23 additions & 0 deletions src/lib/utils/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { NumericRange } from "@sveltejs/kit";
import type { Page } from "@/api/types";
import { MAX_PER_PAGE } from "@/config/config";

export function isErrorCode(status: number): status is NumericRange<400, 599> {
return status >= 400 && status <= 599;
Expand All @@ -9,3 +11,24 @@ export function isRedirectCode(
): status is NumericRange<300, 308> {
return status >= 300 && status <= 308;
}

/**
* Requests the specified URL and paginates through to return all
* results if additional pages are present.
*/
export async function getAll<T>(
url: URL,
perPage: number = MAX_PER_PAGE,
fetch = globalThis.fetch,
): Promise<Array<T>> {
url.searchParams.set("per_page", String(perPage));
const resp = await fetch(url, { credentials: "include" });
const data: Page<T> = await resp.json();
const results = data.results;
if (data.next) {
// Recursively fetch the next page
const nextResults = await getAll<T>(new URL(data.next), perPage, fetch);
return results.concat(nextResults);
}
return results;
}
57 changes: 57 additions & 0 deletions src/routes/app/projects/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
import { goto } from "$app/navigation";
import { page } from "$app/stores";
import MainLayout from "@/lib/components/MainLayout.svelte";
import Button from "@/lib/components/common/Button.svelte";
import SidebarItem from "@/lib/components/sidebar/SidebarItem.svelte";
import Search from "@/lib/components/inputs/Search.svelte";
import { FileDirectory24, People16, Person16 } from "svelte-octicons";
import ContentLayout from "@/lib/components/ContentLayout.svelte";
import PageToolbar from "@/lib/components/common/PageToolbar.svelte";
import Empty from "@/lib/components/common/Empty.svelte";
export let data;
function search(event: SubmitEvent) {
event.preventDefault();
const url = new URL($page.url); // make a copy
const formData = new FormData(event.currentTarget as HTMLFormElement);
const query = formData.get("query") ?? "";
if (!query) return;
url.searchParams.set("query", query as string);
goto(url);
}
$: query = ($page.url as URL).searchParams.get("query") ?? "";
</script>

<MainLayout>
<svelte:fragment slot="navigation">
<Button mode="primary" href="/create">Create Project</Button>
<SidebarItem active={data.list === "owned"} href="?list=owned">
<Person16 />
Your Projects
</SidebarItem>
<SidebarItem active={data.list === "shared"} href="?list=shared">
<People16 />
Shared with you
</SidebarItem>
</svelte:fragment>

<ContentLayout slot="content">
<PageToolbar slot="header">
<Search name="query" {query} on:submit={search} slot="center" />
</PageToolbar>

{#each data.projects as project}
<p>{project.title}</p>
<p>{project.description}</p>
{:else}
<Empty icon={FileDirectory24}>No projects found</Empty>
{/each}

<PageToolbar slot="footer"></PageToolbar>
</ContentLayout>
</MainLayout>
23 changes: 23 additions & 0 deletions src/routes/app/projects/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { getOwned, getShared } from "$lib/api/projects";
import { breadcrumbTrail } from "$lib/utils/navigation";
import type { Project } from "@/api/types";

export async function load({ url, parent, fetch }) {
const { me } = await parent();
const params = Object.fromEntries(url.searchParams.entries());
const list = params.list ?? "owned";
let projects: Project[];
if (list === "owned") {
projects = await getOwned(me.id);
} else if (list === "shared") {
projects = await getShared(me.id);
}
const breadcrumbs = await breadcrumbTrail(parent, [
{ href: "/projects", title: "Projects" },
]);
return {
list,
projects,
breadcrumbs,
};
}
2 changes: 1 addition & 1 deletion src/routes/app/sidebar/Projects.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

<SidebarGroup>
<SidebarItem slot="title"><FileDirectory16 /> Projects</SidebarItem>
<Action slot="action" icon={Book16}>Explore</Action>
<a href="/projects" slot="action"><Action icon={Book16}>Explore</Action></a>
<Flex direction="column" gap={0}>
{#await pinned}
<Empty icon={Hourglass24}>Loading pinned projects…</Empty>
Expand Down

0 comments on commit 2ded65e

Please sign in to comment.