mirror of
https://github.com/pockethost/pockethost.git
synced 2025-11-26 15:35:50 +00:00
enh: dashboard layout update
This commit is contained in:
parent
4fb070699d
commit
d9b5844d80
@ -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>
|
||||
</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>
|
||||
</aside>
|
||||
{/if}
|
||||
</MediaQuery>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -16,5 +16,7 @@
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<InstanceList />
|
||||
</div>
|
||||
</AuthStateGuard>
|
||||
|
||||
@ -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: <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}
|
||||
|
||||
@ -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: <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>
|
||||
Loading…
x
Reference in New Issue
Block a user