enh: dashboard layout update

This commit is contained in:
Ben Allfree 2024-10-04 08:21:49 -07:00
parent 4fb070699d
commit d9b5844d80
5 changed files with 139 additions and 249 deletions

View File

@ -1,15 +1,11 @@
<script lang="ts">
import { page } from '$app/stores'
import Logo from '$components/Logo.svelte'
import MediaQuery from '$components/MediaQuery.svelte'
import { DISCORD_URL, DOCS_URL } from '$src/env'
import { DOCS_URL } from '$src/env'
import { client } from '$src/pocketbase-client'
import InstancesGuard from '$src/routes/InstancesGuard.svelte'
import { globalInstancesStore } from '$util/stores'
import { globalInstancesStore, userStore } from '$util/stores'
import { values } from '@s-libs/micro-dash'
import SubscriptionStatus from './SubscriptionStatus.svelte'
import UserLoggedIn from './helpers/UserLoggedIn.svelte'
import SidebarNavLink from '$components/sidebar/SidebarNavLink.svelte'
import { writable } from 'svelte/store'
type TypeInstanceObject = {
id: string
@ -19,6 +15,35 @@
let arrayOfActiveInstances: TypeInstanceObject[] = []
async function gravatarHash(email: string) {
// Normalize the email by trimming and converting to lowercase
const normalizedEmail = email.trim().toLowerCase()
console.log('normalizedEmail', normalizedEmail)
// Convert the normalized email to a UTF-8 byte array
const msgBuffer = new TextEncoder().encode(normalizedEmail)
// Hash the email using MD5
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer)
// Convert the hash to a hex stringc
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
return hashHex
}
const avatar = writable('')
$: {
if ($userStore?.email) {
gravatarHash($userStore.email).then((hash) => {
avatar.set(`https://www.gravatar.com/avatar/${hash}`)
})
}
}
$: {
if ($globalInstancesStore) {
arrayOfActiveInstances = values($globalInstancesStore).filter(
@ -39,142 +64,76 @@
}
const handleMobileNavDismiss = () => {
document.querySelector<HTMLElement>('.drawer-overlay')?.click()
document.querySelector<HTMLElement>('summary.apps')?.click()
}
</script>
<!-- Custom Tablet Navigation -->
<MediaQuery query="(min-width: 701px) and (max-width: 1024px)" let:matches>
{#if matches}
<UserLoggedIn>
<div role="tablist" class="tabs tabs-boxed">
<div class="navbar bg-base-100">
<div class="flex-1">
<a href="/" role="tab" class="tab">
<Logo hideLogoText={true} logoWidth="h-8" />
</a>
<a
role="tab"
class="tab {$page.url.pathname === '/' ? `tab-active` : ``}"
href="/">Dashboard</a
>
<a href={DISCORD_URL} class="tab" target="_blank" rel="noreferrer"
><i class="fa-regular fa-comment-code mr-2"></i> Support
</a>
<a
href={`${DOCS_URL()}`}
class="tab"
role="tab"
target="_blank"
rel="noreferrer"
>
<i class="fa-regular fa-webhook mr-2"></i> Docs
</a>
<a
role="tab"
class="tab {$page.url.pathname.endsWith(`/account`)
? `tab-active`
: ``}"
href="/account">My Account</a
>
<button type="button" class="tab" on:click={handleLogoutAndRedirect}
><i class="fa-regular fa-arrow-up-left-from-circle mr-2"></i> Logout</button
>
</div>
</UserLoggedIn>
{:else}
<aside
class="lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 lg:flex-col w-full max-w-[360px] h-full"
>
<div class="flex grow flex-col overflow-y-auto bg-gray-900 px-6 h-full">
<div class="flex shrink-0 items-center">
<a href="/" class="" on:click={handleMobileNavDismiss}>
<Logo hideLogoText={true} logoWidth="w-20" />
<div class="hidden md:block ml-1">PocketHost</div>
</a>
</div>
<nav class="flex flex-1 flex-col">
<SubscriptionStatus handleClick={handleMobileNavDismiss} />
<ul role="list" class="-mx-2 space-y-1 mb-8">
<div class="flex-none gap-2">
<ul class="menu menu-horizontal px-1">
<li>
<SidebarNavLink
url="/"
icon="house"
handleClick={handleMobileNavDismiss}>Dashboard</SidebarNavLink
>
<details>
<summary class="apps">Apps</summary>
<ul class="bg-base-100 rounded-t-none p-2">
<InstancesGuard>
<ul role="list" class="ml-6 space-y-1 mt-1 mb-2">
<ul role="list" class="z-50 bg-base-100">
<li>
<a href="/app/new" on:click={handleMobileNavDismiss}
>+ New App</a
>
</li>
{#each arrayOfActiveInstances as app}
<li>
<SidebarNavLink
url={`/app/instances/${app.id}`}
icon="server"
iconSmall={true}
handleClick={handleMobileNavDismiss}
>{app.subdomain}</SidebarNavLink
<a
href={`/app/instances/${app.id}`}
on:click={handleMobileNavDismiss}>{app.subdomain}</a
>
</li>
{/each}
</ul>
<div class="px-4 mb-4">
<a
href="/app/new"
class="btn btn-primary btn-outline btn-block btn-sm"
on:click={handleMobileNavDismiss}
>
<i class="fa-light fa-plus"></i>
Create a New App
</a>
</div>
</InstancesGuard>
</li>
<li>
<SidebarNavLink
url="/account"
icon="user"
handleClick={handleMobileNavDismiss}>My Account</SidebarNavLink
>
</li>
<li>
<SidebarNavLink
url={DISCORD_URL}
icon="comment-code"
external={true}
handleClick={handleMobileNavDismiss}>Support</SidebarNavLink
>
</li>
<li>
<SidebarNavLink
url={DOCS_URL()}
icon="webhook"
external={true}
handleClick={handleMobileNavDismiss}>Docs</SidebarNavLink
>
</li>
<li>
<SidebarNavLink
url="https://github.com/pockethost/pockethost"
icon="github"
brandIcon={true}
external={true}
handleClick={handleMobileNavDismiss}>GitHub</SidebarNavLink
>
</li>
<li>
<SidebarNavLink
url="#"
icon="arrow-up-left-from-circle"
handleClick={handleLogoutAndRedirect}>Logout</SidebarNavLink
>
</ul>
</details>
</li>
</ul>
</nav>
<ul class="menu menu-horizontal px-1">
<li>
<a href="https://pockethost.io/support" target="_blank" rel="noreferrer"
>Support</a
>
</li>
<li>
<a href={`${DOCS_URL()}`} target="_blank" rel="noreferrer">Docs</a>
</li>
<li>
<a
href="https://github.com/pockethost/pockethost"
target="_blank"
rel="noreferrer"
>
<i class="fa-brands fa-github mt-1" />
</a>
</li>
</ul>
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-ghost btn-circle avatar">
<div class="w-10 rounded-full">
<img src={$avatar} />
</div>
</aside>
{/if}
</MediaQuery>
</div>
<ul
tabindex="0"
class="menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow"
>
<li><a href="/account">Settings</a></li>
<li><a on:click={handleLogoutAndRedirect}>Logout</a></li>
</ul>
</div>
</div>
</div>

View File

@ -15,21 +15,12 @@
<AuthStateGuard>
<div>
<UserLoggedIn>
<MediaQuery query="(max-width: 700px)" let:matches>
{#if matches}
<MobileNavDrawer>
<Navbar />
</MobileNavDrawer>
{:else}
<Navbar />
{/if}
</MediaQuery>
</UserLoggedIn>
<main class="py-10 {$isUserLoggedIn ? `lg:pl-72` : ``}">
<main class="">
<div class="px-4 sm:px-6 lg:px-8">
<VerifyAccountBar />
<slot />
</div>
</main>

View File

@ -16,5 +16,7 @@
>
</div>
<div class="flex flex-wrap gap-2">
<InstanceList />
</div>
</AuthStateGuard>

View File

@ -1,55 +1,48 @@
<script lang="ts">
import Card from '$components/cards/Card.svelte'
import CardHeader from '$components/cards/CardHeader.svelte'
import InstanceRow from '$src/routes/dashboard/InstanceRow.svelte'
import { INSTANCE_ADMIN_URL } from '$src/env'
import { globalInstancesStore } from '$util/stores'
import { values } from '@s-libs/micro-dash'
import { InstanceFields } from 'pockethost/common'
let arrayOfActiveInstances: InstanceFields[] = []
let arrayOfMaintenanceInstances: InstanceFields[] = []
$: {
if ($globalInstancesStore) {
arrayOfActiveInstances = values($globalInstancesStore).filter(
(app) => !app.maintenance,
)
arrayOfMaintenanceInstances = values($globalInstancesStore).filter(
(app) => app.maintenance,
)
}
}
</script>
<Card height="h-auto">
<CardHeader>Active Instances</CardHeader>
{#each values($globalInstancesStore).sort( (a, b) => a.subdomain.localeCompare(b.subdomain), ) as instance, index}
<div class="card w-80 bg-neutral m-4">
<div class="card-body">
<div class="card-title">{instance.subdomain}</div>
<div class="grid mb-24">
{#each arrayOfActiveInstances as instance, index}
<InstanceRow {instance} {index} />
{/each}
{#if arrayOfActiveInstances.length === 0}
<p class="italic">
None of your instances are active. Create a new app to use PocketBase!
</p>
{/if}
<div class="flex flex-wrap gap-2">
<div class="badge badge-accent badge-outline">
Status: &nbsp;<span class="capitalize">{instance.status}</span>
</div>
<div class="badge badge-accent badge-outline">
Version: {instance.version}
</div>
<CardHeader>Instances in Maintenance Mode</CardHeader>
<p class="mb-4 opacity-50">
Maintenance Mode will prevent these instances process from running. No
requests are processed while your instance is in Maintenance Mode.
</p>
<div class="grid">
{#each arrayOfMaintenanceInstances as instance, index}
<InstanceRow {instance} {index} />
{/each}
{#if arrayOfMaintenanceInstances.length === 0}
<p class="italic">None of your instances in Maintenance Mode</p>
{#if instance.maintenance}
<div class="badge badge-outline border-warning gap-2">
<i class="fa-regular fa-triangle-person-digging text-warning"></i>
<span class="text-warning">Maintenance Mode</span>
</div>
{/if}
</div>
</Card>
<div class="card-actions">
<a href={`/app/instances/${instance.id}`} class="btn btn-primary">
<i class="fa-regular fa-circle-info"></i>
<span>Details</span>
</a>
<a
class="btn btn-secondary"
href={INSTANCE_ADMIN_URL(instance)}
target="_blank"
>
<img
src="/images/pocketbase-logo.svg"
alt="PocketBase Logo"
class="w-6"
/>
<span>Admin</span>
</a>
</div>
</div>
</div>
{/each}

View File

@ -1,55 +0,0 @@
<script lang="ts">
import { INSTANCE_ADMIN_URL } from '$src/env.js'
import { InstanceFields } from 'pockethost/common'
export let instance: InstanceFields
export let index: number
</script>
<div
class="lg:flex items-center justify-between transition-all duration-500 lg:py-8 py-16 px-4 rounded-2xl {index %
2 ===
0
? ''
: 'bg-base-100'}"
>
<div class="lg:text-left text-center mb-6 lg:mb-0">
<h4 class="font-bold capitalize mb-2">{instance.subdomain}</h4>
<div class="flex items-center flex-wrap justify-center gap-2">
<div class="badge badge-accent badge-outline">
Status: &nbsp;<span class="capitalize">{instance.status}</span>
</div>
<div class="badge badge-accent badge-outline">
Version: {instance.version}
</div>
{#if instance.maintenance}
<div class="badge badge-outline border-warning gap-2">
<i class="fa-regular fa-triangle-person-digging text-warning"></i>
<span class="text-warning">Maintenance Mode</span>
</div>
{/if}
</div>
</div>
<div class="flex items-center justify-center gap-2">
<a href={`/app/instances/${instance.id}`} class="btn btn-primary">
<i class="fa-regular fa-circle-info"></i>
<span>Details</span>
</a>
<a
class="btn btn-secondary"
href={INSTANCE_ADMIN_URL(instance)}
target="_blank"
>
<img
src="/images/pocketbase-logo.svg"
alt="PocketBase Logo"
class="w-6"
/>
<span>Admin</span>
</a>
</div>
</div>