1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
<script lang="ts">
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([]);
onMount(() => {
let i = 0;
const tempProjects = [];
while (i < 101) {
tempProjects.push({
id: crypto.randomUUID(),
name: faker.word.preposition(),
start: Temporal.Now.plainDateISO(),
description: faker.lorem.words(3),
members: [],
status: ProjectStatus.IDLE,
});
i++;
}
projects.set(tempProjects);
});
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", plugins: { sort: { disable: true } } }),
]);
const { headerRows, rows, tableAttrs, tableBodyAttrs, pluginStates } = table.createViewModel(columns);
const { filterValue } = pluginStates.filter;
</script>
<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 inline-flex gap-1 sm:flex-none">
<Input icon={MagnifyingGlassIcon} placeholder="Search" bind:value={$filterValue} />
<Button text="Create project" />
</div>
</div>
<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} class="shadow-sm">
{#each headerRow.cells as cell (cell.id)}
<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}
</tr>
</Subscribe>
{/each}
</thead>
<tbody {...$tableBodyAttrs} class="divide-y divide-gray-200 bg-white">
{#each $rows as row (row.id)}
<Subscribe rowAttrs={row.attrs()} let:rowAttrs>
<tr {...rowAttrs}>
{#each row.cells as cell (cell.id)}
{@const materialisedCell = cell.render()}
<Subscribe attrs={cell.attrs()} let:attrs>
<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}
</tr>
</Subscribe>
{/each}
</tbody>
</table>
</div>
</div>
|