added card component and workspace display

This commit is contained in:
xyroscar
2025-11-24 04:25:53 -08:00
parent f51fa6ea51
commit 0e342a54be
11 changed files with 257 additions and 21 deletions

View File

@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-action"
class={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
{...restProps}
>
{@render children?.()}
</div>

View File

@@ -0,0 +1,15 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div bind:this={ref} data-slot="card-content" class={cn("px-6", className)} {...restProps}>
{@render children?.()}
</div>

View File

@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
</script>
<p
bind:this={ref}
data-slot="card-description"
class={cn("text-muted-foreground text-sm", className)}
{...restProps}
>
{@render children?.()}
</p>

View File

@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-footer"
class={cn("[.border-t]:pt-6 flex items-center px-6", className)}
{...restProps}
>
{@render children?.()}
</div>

View File

@@ -0,0 +1,23 @@
<script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-header"
class={cn(
"@container/card-header has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6 grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6",
className
)}
{...restProps}
>
{@render children?.()}
</div>

View File

@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-title"
class={cn("font-semibold leading-none", className)}
{...restProps}
>
{@render children?.()}
</div>

View File

@@ -0,0 +1,23 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card"
class={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
className
)}
{...restProps}
>
{@render children?.()}
</div>

View File

@@ -0,0 +1,25 @@
import Root from "./card.svelte";
import Content from "./card-content.svelte";
import Description from "./card-description.svelte";
import Footer from "./card-footer.svelte";
import Header from "./card-header.svelte";
import Title from "./card-title.svelte";
import Action from "./card-action.svelte";
export {
Root,
Content,
Description,
Footer,
Header,
Title,
Action,
//
Root as Card,
Content as CardContent,
Description as CardDescription,
Footer as CardFooter,
Header as CardHeader,
Title as CardTitle,
Action as CardAction,
};

View File

@@ -1,5 +1,33 @@
import type { Workspace } from "$lib/types/workspace"; import type { Workspace } from "$lib/types/workspace";
let ws: Workspace[] = [
{
Id: "1",
Name: "Test 1",
Description: "This is a test description"
},
{
Id: "2",
Name: "Test 2",
Description: "This is a longer test description"
},
{
Id: "3",
Name: "Test 3",
Description: "This is a slightly longer test description"
},
{
Id: "4",
Name: "Test 4",
Description: "This is an even slightly longer test description"
},
{
Id: "5",
Name: "Test 5",
Description: "This is a veryyyyyyyyyyyyyyyyyyyyyyyyyyy longggggggggggggggggggggggggggg test descriptionnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
},
]
export async function get_workspaces(): Promise<Workspace[]> { export async function get_workspaces(): Promise<Workspace[]> {
return [] return ws
} }

View File

@@ -1,4 +1,5 @@
export type Workspace = { export type Workspace = {
Id: string, Id: string;
Name: string, Name: string;
} Description: string;
};

View File

@@ -3,24 +3,65 @@
import { Button } from "$lib/components/ui/button/index.js"; import { Button } from "$lib/components/ui/button/index.js";
import FolderCodeIcon from "@lucide/svelte/icons/folder-code"; import FolderCodeIcon from "@lucide/svelte/icons/folder-code";
import { get_workspaces } from "$lib/services/workspaces"; import { get_workspaces } from "$lib/services/workspaces";
import type { Workspace } from "$lib/types/workspace";
import * as Card from "$lib/components/ui/card/index";
get_workspaces; let showPrompt = false;
let workspaces: Workspace[] = [];
get_workspaces().then((ws) => {
if (ws.length == 0) {
showPrompt = true;
} else {
workspaces = ws;
}
});
</script> </script>
<Empty.Root class="flex min-h-[calc(100vh-4rem)] items-center justify-center"> {#if showPrompt}
<Empty.Header> <Empty.Root class="flex min-h-[calc(100vh-4rem)] items-center justify-center">
<Empty.Media variant="icon"> <Empty.Header>
<FolderCodeIcon /> <Empty.Media variant="icon">
</Empty.Media> <FolderCodeIcon />
<Empty.Title>No Workspaces Yet</Empty.Title> </Empty.Media>
<Empty.Description> <Empty.Title>No Workspaces Yet</Empty.Title>
You haven't created any Workspaces yet. Get started by creating your first <Empty.Description>
project. You haven't created any Workspaces yet. Get started by creating your
</Empty.Description> first project.
</Empty.Header> </Empty.Description>
<Empty.Content> </Empty.Header>
<div class="flex gap-2"> <Empty.Content>
<Button>Create Workspace</Button> <div class="flex gap-2">
<Button>Create Workspace</Button>
</div>
</Empty.Content>
</Empty.Root>
{:else}
<div class="min-h-[calc(100vh-4rem)] p-6">
<div
class="grid gap-6
grid-cols-1
sm:grid-cols-2
md:grid-cols-3
xl:grid-cols-4
2xl:grid-cols-5"
>
{#each workspaces as workspace (workspace.Id)}
<Card.Root
class="min-h-40 w-full max-w-xs mx-auto flex flex-col justify-between cursor-pointer hover:shadow-md transition-shadow"
>
<Card.Header>
<Card.Title class="truncate">{workspace.Name}</Card.Title>
<Card.Description class="text-xs text-muted-foreground">
{workspace.Description}
</Card.Description>
</Card.Header>
<Card.Footer class="flex items-center justify-center gap-2">
<Button size="sm" class="justify-center">Open</Button>
<Button size="sm" class="justify-center">Edit</Button>
</Card.Footer>
</Card.Root>
{/each}
</div> </div>
</Empty.Content> </div>
</Empty.Root> {/if}