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">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores'
|
|
||||||
import Logo from '$components/Logo.svelte'
|
import Logo from '$components/Logo.svelte'
|
||||||
import MediaQuery from '$components/MediaQuery.svelte'
|
import { DOCS_URL } from '$src/env'
|
||||||
import { DISCORD_URL, DOCS_URL } from '$src/env'
|
|
||||||
import { client } from '$src/pocketbase-client'
|
import { client } from '$src/pocketbase-client'
|
||||||
import InstancesGuard from '$src/routes/InstancesGuard.svelte'
|
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 { values } from '@s-libs/micro-dash'
|
||||||
import SubscriptionStatus from './SubscriptionStatus.svelte'
|
import { writable } from 'svelte/store'
|
||||||
import UserLoggedIn from './helpers/UserLoggedIn.svelte'
|
|
||||||
import SidebarNavLink from '$components/sidebar/SidebarNavLink.svelte'
|
|
||||||
|
|
||||||
type TypeInstanceObject = {
|
type TypeInstanceObject = {
|
||||||
id: string
|
id: string
|
||||||
@ -19,6 +15,35 @@
|
|||||||
|
|
||||||
let arrayOfActiveInstances: TypeInstanceObject[] = []
|
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) {
|
if ($globalInstancesStore) {
|
||||||
arrayOfActiveInstances = values($globalInstancesStore).filter(
|
arrayOfActiveInstances = values($globalInstancesStore).filter(
|
||||||
@ -39,142 +64,76 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleMobileNavDismiss = () => {
|
const handleMobileNavDismiss = () => {
|
||||||
document.querySelector<HTMLElement>('.drawer-overlay')?.click()
|
document.querySelector<HTMLElement>('summary.apps')?.click()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Custom Tablet Navigation -->
|
<div class="navbar bg-base-100">
|
||||||
<MediaQuery query="(min-width: 701px) and (max-width: 1024px)" let:matches>
|
<div class="flex-1">
|
||||||
{#if matches}
|
|
||||||
<UserLoggedIn>
|
|
||||||
<div role="tablist" class="tabs tabs-boxed">
|
|
||||||
<a href="/" role="tab" class="tab">
|
<a href="/" role="tab" class="tab">
|
||||||
<Logo hideLogoText={true} logoWidth="h-8" />
|
<Logo hideLogoText={true} logoWidth="h-8" />
|
||||||
</a>
|
<div class="hidden md:block ml-1">PocketHost</div>
|
||||||
|
|
||||||
<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" />
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex-none gap-2">
|
||||||
<nav class="flex flex-1 flex-col">
|
<ul class="menu menu-horizontal px-1">
|
||||||
<SubscriptionStatus handleClick={handleMobileNavDismiss} />
|
|
||||||
|
|
||||||
<ul role="list" class="-mx-2 space-y-1 mb-8">
|
|
||||||
<li>
|
<li>
|
||||||
<SidebarNavLink
|
<details>
|
||||||
url="/"
|
<summary class="apps">Apps</summary>
|
||||||
icon="house"
|
<ul class="bg-base-100 rounded-t-none p-2">
|
||||||
handleClick={handleMobileNavDismiss}>Dashboard</SidebarNavLink
|
|
||||||
>
|
|
||||||
|
|
||||||
<InstancesGuard>
|
<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}
|
{#each arrayOfActiveInstances as app}
|
||||||
<li>
|
<li>
|
||||||
<SidebarNavLink
|
<a
|
||||||
url={`/app/instances/${app.id}`}
|
href={`/app/instances/${app.id}`}
|
||||||
icon="server"
|
on:click={handleMobileNavDismiss}>{app.subdomain}</a
|
||||||
iconSmall={true}
|
|
||||||
handleClick={handleMobileNavDismiss}
|
|
||||||
>{app.subdomain}</SidebarNavLink
|
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</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>
|
</InstancesGuard>
|
||||||
</li>
|
</ul>
|
||||||
<li>
|
</details>
|
||||||
<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
|
|
||||||
>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</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>
|
||||||
</aside>
|
</div>
|
||||||
{/if}
|
<ul
|
||||||
</MediaQuery>
|
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>
|
||||||
|
|||||||
@ -15,21 +15,12 @@
|
|||||||
<AuthStateGuard>
|
<AuthStateGuard>
|
||||||
<div>
|
<div>
|
||||||
<UserLoggedIn>
|
<UserLoggedIn>
|
||||||
<MediaQuery query="(max-width: 700px)" let:matches>
|
|
||||||
{#if matches}
|
|
||||||
<MobileNavDrawer>
|
|
||||||
<Navbar />
|
<Navbar />
|
||||||
</MobileNavDrawer>
|
|
||||||
{:else}
|
|
||||||
<Navbar />
|
|
||||||
{/if}
|
|
||||||
</MediaQuery>
|
|
||||||
</UserLoggedIn>
|
</UserLoggedIn>
|
||||||
|
|
||||||
<main class="py-10 {$isUserLoggedIn ? `lg:pl-72` : ``}">
|
<main class="">
|
||||||
<div class="px-4 sm:px-6 lg:px-8">
|
<div class="px-4 sm:px-6 lg:px-8">
|
||||||
<VerifyAccountBar />
|
<VerifyAccountBar />
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@ -16,5 +16,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
<InstanceList />
|
<InstanceList />
|
||||||
|
</div>
|
||||||
</AuthStateGuard>
|
</AuthStateGuard>
|
||||||
|
|||||||
@ -1,55 +1,48 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Card from '$components/cards/Card.svelte'
|
import { INSTANCE_ADMIN_URL } from '$src/env'
|
||||||
import CardHeader from '$components/cards/CardHeader.svelte'
|
|
||||||
import InstanceRow from '$src/routes/dashboard/InstanceRow.svelte'
|
|
||||||
import { globalInstancesStore } from '$util/stores'
|
import { globalInstancesStore } from '$util/stores'
|
||||||
import { values } from '@s-libs/micro-dash'
|
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>
|
</script>
|
||||||
|
|
||||||
<Card height="h-auto">
|
{#each values($globalInstancesStore).sort( (a, b) => a.subdomain.localeCompare(b.subdomain), ) as instance, index}
|
||||||
<CardHeader>Active Instances</CardHeader>
|
<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">
|
<div class="flex flex-wrap gap-2">
|
||||||
{#each arrayOfActiveInstances as instance, index}
|
<div class="badge badge-accent badge-outline">
|
||||||
<InstanceRow {instance} {index} />
|
Status: <span class="capitalize">{instance.status}</span>
|
||||||
{/each}
|
</div>
|
||||||
|
<div class="badge badge-accent badge-outline">
|
||||||
{#if arrayOfActiveInstances.length === 0}
|
Version: {instance.version}
|
||||||
<p class="italic">
|
|
||||||
None of your instances are active. Create a new app to use PocketBase!
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CardHeader>Instances in Maintenance Mode</CardHeader>
|
{#if instance.maintenance}
|
||||||
|
<div class="badge badge-outline border-warning gap-2">
|
||||||
<p class="mb-4 opacity-50">
|
<i class="fa-regular fa-triangle-person-digging text-warning"></i>
|
||||||
Maintenance Mode will prevent these instances process from running. No
|
<span class="text-warning">Maintenance Mode</span>
|
||||||
requests are processed while your instance is in Maintenance Mode.
|
</div>
|
||||||
</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}
|
{/if}
|
||||||
</div>
|
</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