aboutsummaryrefslogtreecommitdiffstats
path: root/code/app/src/routes
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-10-11 06:57:36 +0200
committerivarlovlie <git@ivarlovlie.no>2022-10-11 06:57:36 +0200
commit4e8f0c96904f9915c6bc6e50f12e5094f924759c (patch)
treeedbfb45a80878636fa04afe516e892ed4aefd594 /code/app/src/routes
parentafbe98090a90ae7770c3a319b8a30c1d23cfbbc3 (diff)
downloadgreatoffice-4e8f0c96904f9915c6bc6e50f12e5094f924759c.tar.xz
greatoffice-4e8f0c96904f9915c6bc6e50f12e5094f924759c.zip
feat: !WIP projects table
- Sorting - Filtering - Project status badge
Diffstat (limited to 'code/app/src/routes')
-rw-r--r--code/app/src/routes/(main)/(app)/projects/+page.svelte75
-rw-r--r--code/app/src/routes/(main)/(app)/projects/[id]/+page.svelte5
2 files changed, 67 insertions, 13 deletions
diff --git a/code/app/src/routes/(main)/(app)/projects/+page.svelte b/code/app/src/routes/(main)/(app)/projects/+page.svelte
index ad273ab..ceb08b4 100644
--- a/code/app/src/routes/(main)/(app)/projects/+page.svelte
+++ b/code/app/src/routes/(main)/(app)/projects/+page.svelte
@@ -1,13 +1,16 @@
<script lang="ts">
- import { Button } from "$lib/components";
+ import { Button, ProjectStatusBadge, Input } from "$lib/components";
import type { Project } from "$lib/models/projects/Project";
import { onMount } from "svelte";
import { faker } from "@faker-js/faker";
import { Temporal } from "temporal-polyfill";
import { createTable, Subscribe, Render } from "svelte-headless-table";
+ import { addSortBy, addTableFilter } from "svelte-headless-table/plugins";
import { ProjectStatus } from "$lib/models/projects/ProjectStatus";
import { writable, type Writable } from "svelte/store";
+ import { ChevronDownIcon, ChevronUpIcon, ChevronUpDownIcon, MagnifyingGlassIcon, FunnelIcon } from "$lib/components/icons";
import LL from "$lib/i18n/i18n-svelte";
+ import { goto } from "$app/navigation";
let projects: Writable<Array<Project>> = writable([]);
@@ -28,37 +31,75 @@
projects.set(tempProjects);
});
- const table = createTable(projects);
+ function goto_project(name: string) {
+ const projectId = $projects.find((p) => p.name === name).id;
+ goto("/projects/" + projectId);
+ }
+
+ const table = createTable(projects, {
+ sort: addSortBy(),
+ filter: addTableFilter(),
+ });
+
const columns = table.createColumns([
table.column({ header: $LL.name(), accessor: "name" }),
table.column({ header: "Status", accessor: "status" }),
table.column({ header: "Start", accessor: "start" }),
- table.column({ header: "Description", accessor: "description" }),
+ table.column({ header: "Description", accessor: "description", plugins: { sort: { disable: true } } }),
]);
- const { headerRows, rows, tableAttrs, tableBodyAttrs } = table.createViewModel(columns);
+ const { headerRows, rows, tableAttrs, tableBodyAttrs, pluginStates } = table.createViewModel(columns);
+ const { filterValue } = pluginStates.filter;
</script>
-<div class="px-4 sm:px-6 lg:px-8">
+<div class="py-2 px-5">
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h1 class="text-xl font-semibold text-gray-900">Projects</h1>
<p class="mt-2 text-sm text-gray-700">A list of all the projects in your organsation.</p>
</div>
- <div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
+ <div class="mt-4 sm:mt-0 sm:ml-16 inline-flex gap-1 sm:flex-none">
+ <Input icon={MagnifyingGlassIcon} placeholder="Search" bind:value={$filterValue} />
<Button text="Create project" />
</div>
</div>
- <div class="-mx-4 mt-8 overflow-hidden sm:-mx-6 md:mx-0 MuonW PowerTable">
+ <div class="-mx-2 mt-6 rounded-md shadow overflow-auto max-h-[80vh] sm:-mx-6 md:mx-0">
<table {...$tableAttrs} class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
{#each $headerRows as headerRow (headerRow.id)}
<Subscribe rowAttrs={headerRow.attrs()} let:rowAttrs>
- <tr {...rowAttrs}>
+ <tr {...rowAttrs} class="shadow-sm">
{#each headerRow.cells as cell (cell.id)}
- <Subscribe attrs={cell.attrs()} let:attrs>
- <th {...attrs} class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
- <Render of={cell.render()} />
+ <Subscribe attrs={cell.attrs()} let:attrs props={cell.props()} let:props>
+ <th
+ {...attrs}
+ scope="col"
+ class="sticky top-0 bg-gray-50 bg-opacity-100 whitespace-nowrap px-2 py-3.5 text-left text-sm font-semibold text-gray-900"
+ >
+ <div class="group inline-flex">
+ <Render of={cell.render()} />
+ <span
+ on:click={props.sort.toggle}
+ class="{props.sort.disabled
+ ? 'bg-gray-200 text-gray-900 group-hover:bg-gray-300'
+ : 'invisible text-gray-400 group-hover:visible group-focus:visible'}
+ {props.sort.disabled ? '' : 'cursor-pointer'}
+ ml-2 flex-none rounded"
+ >
+ {#if props.sort.order === "asc"}
+ <ChevronUpIcon />
+ {:else if props.sort.order === "desc"}
+ <ChevronDownIcon />
+ {:else if !props.sort.disabled}
+ <ChevronUpDownIcon />
+ {/if}
+ </span>
+ {#if cell.id === "status"}
+ <span class="invisible text-gray-400 cursor-pointer group-hover:visible group-focus:visible ml-2 flex-none rounded">
+ <FunnelIcon />
+ </span>
+ {/if}
+ </div>
</th>
</Subscribe>
{/each}
@@ -73,8 +114,16 @@
{#each row.cells as cell (cell.id)}
{@const materialisedCell = cell.render()}
<Subscribe attrs={cell.attrs()} let:attrs>
- <td class="w-full max-w-0 py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:w-auto sm:max-w-none sm:pl-6" {...attrs}>
- <Render of={materialisedCell} />
+ <td {...attrs} class="whitespace-nowrap px-2 py-2 text-sm">
+ {#if cell.id === "name"}
+ <span class="link" title="Open project" on:click={() => goto_project(materialisedCell.toString())}>
+ <Render of={materialisedCell} />
+ </span>
+ {:else if cell.id === "status"}
+ <ProjectStatusBadge status={materialisedCell.toString()} />
+ {:else}
+ <Render of={materialisedCell} />
+ {/if}
</td>
</Subscribe>
{/each}
diff --git a/code/app/src/routes/(main)/(app)/projects/[id]/+page.svelte b/code/app/src/routes/(main)/(app)/projects/[id]/+page.svelte
new file mode 100644
index 0000000..ca474e2
--- /dev/null
+++ b/code/app/src/routes/(main)/(app)/projects/[id]/+page.svelte
@@ -0,0 +1,5 @@
+<script lang="ts">
+ import { page } from "$app/stores";
+</script>
+
+<h1>{$page.params.id}</h1>