Skip to content

Commit

Permalink
chore: improve dashboard with sidebar layout (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
edwinkys authored Nov 19, 2024
2 parents be24a54 + ec9f161 commit 5fd4473
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 70 deletions.
11 changes: 11 additions & 0 deletions dashboard/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,14 @@
font-family: "Inconsolata", monospace;
}
}

@layer components {
.control-button {
@apply p-1 rounded text-gray-500;
@apply transition-colors duration-300;
}

.control-button:hover {
@apply bg-black bg-opacity-5;
}
}
4 changes: 2 additions & 2 deletions dashboard/src/lib/components/modals/basic.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<style lang="postcss">
.modal-container {
@apply flex items-center justify-center;
@apply fixed top-0 left-0 w-screen h-dvh z-[1];
@apply fixed top-0 left-0 w-screen h-dvh z-10;
}
.backdrop {
Expand All @@ -31,7 +31,7 @@
}
.modal {
@apply flex flex-col p-6 z-[2];
@apply flex flex-col p-6 z-[11];
@apply bg-white w-full h-full;
}
Expand Down
79 changes: 79 additions & 0 deletions dashboard/src/lib/components/navs/header.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<script lang="ts">
import type { Approver } from "$lib/types"
import { approver } from "$lib/store"
import BasicModal from "$lib/components/modals/basic.svelte"
import InputField from "$lib/components/inputs/field.svelte"
import BasicButton from "$lib/components/buttons/basic.svelte"
import { SidePanelOpenFilled, UserAvatarFilled } from "carbon-icons-svelte"
export let openSidebar: () => void
let showApproverModal = !$approver
let approverName = $approver ? $approver.name : ""
let approverEmail = $approver ? $approver.email : ""
function saveProfile(name: string, email: string) {
if (!name || !email) {
return
}
let profile: Approver = {
name: name,
email: email
}
approver.set(profile)
window.localStorage.setItem("approver", JSON.stringify($approver))
showApproverModal = false
}
</script>

<header class="header space-x-4">
<button class="control-button" on:click={openSidebar}>
<SidePanelOpenFilled size={24} />
</button>
<button
class="control-button"
on:click={() => {
showApproverModal = true
}}
>
<UserAvatarFilled size={24} />
</button>
</header>

<BasicModal bind:show={showApproverModal}>
<div class="flex flex-col space-y-6">
<div class="flex flex-col space-y-3">
<h3>Approver Profile</h3>
<small>
This information adds more context to the approval response.
</small>
</div>
<div class="flex flex-col space-y-3">
<InputField
id="name"
label="Full Name"
placeholder="Justin Case"
bind:value={approverName}
/>
<InputField
id="email"
label="Email Address"
placeholder="[email protected]"
bind:value={approverEmail}
/>
</div>
<BasicButton
text="Save Profile"
action={() => saveProfile(approverName, approverEmail)}
/>
</div>
</BasicModal>

<style lang="postcss">
.header {
@apply flex items-center justify-between p-4 bg-gray-50;
@apply absolute top-0 left-0 w-full z-[4];
height: 80px;
}
</style>
93 changes: 93 additions & 0 deletions dashboard/src/lib/components/navs/sidebar.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<script lang="ts">
import type { ComponentType } from "svelte"
import { fade, slide } from "svelte/transition"
import Wordmark from "$lib/components/utils/wordmark.svelte"
import { SidePanelCloseFilled, Home, LogoGithub } from "carbon-icons-svelte"
export let open: boolean = true
export let close: () => void
type Navigation = {
icon: ComponentType
label: string
route: string
}
const navigation: Navigation[] = [
{
icon: Home,
label: "Home",
route: "/"
},
{
icon: LogoGithub,
label: "Repository",
route: "https://github.com/phantasmlabs/phantasm"
}
]
</script>

{#if open}
<div
class="overlay"
on:click={close}
role="none"
transition:fade={{ duration: 100 }}
/>
<nav class="sidebar" transition:slide={{ axis: "x", duration: 250 }}>
<div class="control-section space-x-4">
<Wordmark size="sm" />
<button class="control-button" on:click={close}>
<SidePanelCloseFilled size={24} />
</button>
</div>
<div class="flex flex-col p-4 space-y-1">
{#each navigation as nav}
<a
href={nav.route}
class="navigation-link space-x-3"
target={nav.route.startsWith("http") ? "_blank" : null}
>
<svelte:component this={nav.icon} size={20} class="flex-none" />
<span>{nav.label}</span>
</a>
{/each}
</div>
</nav>
{/if}

<style lang="postcss">
.sidebar {
@apply flex flex-col flex-none fixed top-0 left-0;
@apply bg-white h-dvh z-[6];
width: 320px;
}
.overlay {
@apply fixed top-0 left-0 w-dvw h-dvh;
@apply bg-black bg-opacity-50 z-[5];
}
.control-section {
@apply flex items-center justify-between p-4;
height: 80px;
}
.navigation-link {
@apply flex items-center p-2 rounded;
}
.navigation-link:hover {
@apply bg-gray-100;
}
@screen lg {
.sidebar {
@apply static;
}
.overlay {
@apply hidden;
}
}
</style>
2 changes: 1 addition & 1 deletion dashboard/src/lib/components/utils/wordmark.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
let fontClass = fontClasses[size]
</script>

<div class="flex items-center">
<div class="flex items-center flex-none">
<img src="/imgs/favicon256.png" alt="Logo" class={logoClass} />
<span class="font-bold {fontClass}">Phantasm</span>
</div>
92 changes: 34 additions & 58 deletions dashboard/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@
import { flip } from "svelte/animate"
import { onMount } from "svelte"
import { connections, alerts, approver } from "$lib/store"
import type { Approver } from "$lib/types"
import { fly } from "svelte/transition"
import Alert from "$lib/components/cards/alert.svelte"
import BasicModal from "$lib/components/modals/basic.svelte"
import InputField from "$lib/components/inputs/field.svelte"
import BasicButton from "$lib/components/buttons/basic.svelte"
import Sidebar from "$lib/components/navs/sidebar.svelte"
import Header from "$lib/components/navs/header.svelte"
const animationDuration = 200
let hydrated = false
let showApproverModal = false
let approverName = ""
let approverEmail = ""
let showSidebar = true
onMount(() => {
let storedConnections = window.localStorage.getItem("connections")
Expand All @@ -31,33 +29,44 @@
approver.set(JSON.parse(storedApprover))
}
showApproverModal = !$approver
hydrated = true
})
function removeAlert(id: string) {
alerts.update((alerts) => alerts.filter((alert) => alert.id !== id))
}
function saveProfile(name: string, email: string) {
if (!name || !email) {
return
}
let profile: Approver = {
name: name,
email: email
}
approver.set(profile)
window.localStorage.setItem("approver", JSON.stringify($approver))
showApproverModal = false
}
</script>

{#if hydrated}
<div class="flex bg-gray-100">
<Sidebar
open={showSidebar}
close={() => {
showSidebar = false
}}
/>
<div class="relative w-full">
<Header
openSidebar={() => {
showSidebar = true
}}
/>
<main class="h-dvh overflow-y-auto">
<!-- This pads the layout in place of the header. -->
<div class="h-[80px]" />
<slot />
</main>
</div>
</div>
{/if}

<div class="fixed bottom-0 right-0 space-y-3 p-6 w-full md:w-[480px]">
{#each $alerts as alert (alert.id)}
<div animate:flip={{ duration: 200 }}>
<div
animate:flip={{ duration: animationDuration }}
in:fly={{ x: -100, duration: animationDuration }}
out:fly={{ x: 300, duration: animationDuration }}
>
<Alert
{alert}
remove={() => {
Expand All @@ -67,36 +76,3 @@
</div>
{/each}
</div>

{#if hydrated}
<slot />

<BasicModal bind:show={showApproverModal}>
<div class="flex flex-col space-y-6">
<div class="flex flex-col space-y-3">
<h3>Approver Profile</h3>
<small>
This information adds more context to the approval response.
</small>
</div>
<div class="flex flex-col space-y-3">
<InputField
id="name"
label="Full Name"
placeholder="Justin Case"
bind:value={approverName}
/>
<InputField
id="email"
label="Email Address"
placeholder="[email protected]"
bind:value={approverEmail}
/>
</div>
<BasicButton
text="Save Profile"
action={() => saveProfile(approverName, approverEmail)}
/>
</div>
</BasicModal>
{/if}
10 changes: 3 additions & 7 deletions dashboard/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import { flip } from "svelte/animate"
import { connections } from "$lib/store"
import type { Connection } from "$lib/types"
import Wordmark from "$lib/components/utils/wordmark.svelte"
import Title from "$lib/components/utils/title.svelte"
import ActionButton from "$lib/components/buttons/action.svelte"
import BasicButton from "$lib/components/buttons/basic.svelte"
Expand Down Expand Up @@ -95,16 +94,13 @@
</BasicModal>

<div class="max-w-screen-sm mx-auto px-6">
<div class="py-24 space-y-12">
<div class="flex items-center justify-center">
<Wordmark size="lg" />
</div>
<div class="py-24 space-y-9">
{#if $connections.length == 0}
<div class="flex flex-col items-center text-center space-y-3">
<h3>It looks quite empty in here!</h3>
<p class="max-w-md">
Add a connection to a Phantasm server to start receiving approval
requests from your AI agents.
Add your first connection to a Phantasm server to start receiving
approval requests from your AI agents.
</p>
</div>
{:else}
Expand Down
2 changes: 0 additions & 2 deletions dashboard/src/routes/connections/[id]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import { connections, alerts } from "$lib/store"
import { goto } from "$app/navigation"
import Title from "$lib/components/utils/title.svelte"
import Wordmark from "$lib/components/utils/wordmark.svelte"
import ApprovalCard from "$lib/components/cards/approval.svelte"
export let data: PageData
Expand Down Expand Up @@ -105,7 +104,6 @@
{#if connected}
<div class="max-w-screen-sm mx-auto px-6 py-24">
<div class="flex flex-col space-y-6">
<div class="-ml-2"><Wordmark size="md" /></div>
<h1>Approval Requests</h1>
{#if requests.length == 0}
<p>There is no approval request to review at the moment.</p>
Expand Down

0 comments on commit 5fd4473

Please sign in to comment.