summaryrefslogtreecommitdiffhomepage
path: root/src/features/workspaces/ui/WorkspacesHome.svelte
blob: bdfd0237f11b0e93b42717e87508a3953c39a3bd (plain)
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
<script lang="ts">
	import { onMount } from "svelte";
	import type { ComputerEntry } from "@dispatch/wire";
	import type { WorkspaceStore } from "../store.svelte";
	import { isValidSlug, workspacePath } from "../logic/route";
	import WorkspaceCard from "./WorkspaceCard.svelte";

	let {
		store,
		onNavigate,
		computers,
	}: { store: WorkspaceStore; onNavigate: (path: string) => void; computers: readonly ComputerEntry[] } =
		$props();

	onMount(() => {
		void store.refresh();
	});

	let newSlug = $state("");
	let slugError = $state<string | null>(null);

	const slugValid = $derived(newSlug.length > 0 && isValidSlug(newSlug));

	function createWorkspace(): void {
		const slug = newSlug.trim();
		if (!isValidSlug(slug)) {
			slugError = "Lowercase letters, digits, and hyphens (1–40 chars).";
			return;
		}
		slugError = null;
		newSlug = "";
		onNavigate(workspacePath(slug));
	}
</script>

<div class="mx-auto flex h-screen w-full max-w-3xl flex-col gap-4 p-6">
	<header class="flex items-center justify-between">
		<h1 class="text-2xl font-bold">Workspaces</h1>
		<a
			href="/default"
			class="btn btn-ghost btn-sm"
			onclick={(e) => {
				e.preventDefault();
				onNavigate("/default");
			}}
		>
			Default
		</a>
	</header>

	<form
		class="flex items-end gap-2"
		onsubmit={(e) => {
			e.preventDefault();
			createWorkspace();
		}}
	>
		<div class="flex-1">
			<label for="new-ws" class="mb-1 block text-xs font-semibold uppercase opacity-60">New workspace</label>
			<input
				id="new-ws"
				class="input input-bordered w-full"
				placeholder="my-workspace"
				bind:value={newSlug}
				autocomplete="off"
				spellcheck="false"
			/>
		</div>
		<button type="submit" class="btn btn-primary btn-sm" disabled={!slugValid}>Create</button>
	</form>
	{#if slugError}
		<p class="text-xs text-error">{slugError}</p>
	{/if}

	<div class="flex-1 overflow-y-auto">
		{#if store.loading && store.list.length === 0}
			<div class="flex h-32 items-center justify-center">
				<span class="loading loading-spinner loading-sm opacity-60"></span>
			</div>
		{:else if store.list.length === 0}
			<p class="py-8 text-center text-sm opacity-60">
				No workspaces yet. Create one above or visit <code>/your-name</code> in the URL.
			</p>
		{:else}
			<ul class="flex flex-col gap-2">
				{#each store.list as ws (ws.id)}
					<WorkspaceCard {ws} {store} {onNavigate} {computers} />
				{/each}
			</ul>
		{/if}
	</div>
</div>