sveltestrap

This commit is contained in:
Ben Allfree 2022-09-19 11:23:06 -07:00
parent 716f379395
commit c8c8e53a10
25 changed files with 553 additions and 396 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.DS_Store .DS_Store
node_modules node_modules
.secret .secret
.vscode

View File

@ -16,6 +16,7 @@
], ],
"prettier": { "prettier": {
"semi": false, "semi": false,
"useTabs": false,
"singleQuote": true, "singleQuote": true,
"plugins": [ "plugins": [
"./node_modules/prettier-plugin-organize-imports", "./node_modules/prettier-plugin-organize-imports",

View File

@ -1 +0,0 @@
export const isBrowser = () => typeof window !== 'undefined'

View File

@ -1,5 +1,5 @@
{ {
"useTabs": true, "useTabs": false,
"singleQuote": true, "singleQuote": true,
"semi": false, "semi": false,
"trailingComma": "none", "trailingComma": "none",

View File

@ -34,6 +34,7 @@
"sass": "^1.54.9", "sass": "^1.54.9",
"svelte-fa": "^3.0.3", "svelte-fa": "^3.0.3",
"svelte-highlight": "^6.2.1", "svelte-highlight": "^6.2.1",
"sveltestrap": "^5.9.0",
"ts-brand": "^0.0.2" "ts-brand": "^0.0.2"
} }
} }

View File

@ -3,7 +3,6 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" href="%sveltekit.assets%/favicon.png" />
<link rel="stylesheet" href="%sveltekit.assets%/global.css" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
%sveltekit.head% %sveltekit.head%
</head> </head>

View File

@ -1,9 +1,10 @@
<script lang="ts"> <script lang="ts">
import { isLoggedIn, user } from '@pockethost/common/src/pocketbase' import { browser } from '$app/environment'
import Button from './Button/Button.svelte' import { redirect } from '$util/redirect'
import { isLoggedIn } from '@pockethost/common/src/pocketbase'
if (typeof window !== 'undefined' && isLoggedIn()) { if (browser && isLoggedIn()) {
window.location.href = '/dashboard' redirect(`/dashboard`)
} }
</script> </script>

View File

@ -1,50 +1,22 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation' import { goto } from '$app/navigation'
import { ButtonStyles } from './types' import { Button } from 'sveltestrap'
import { ButtonStyles } from './types'
export let style: ButtonStyles = ButtonStyles.Wide export let style: ButtonStyles = ButtonStyles.Wide
export let disabled = false export let disabled = false
export let href: string = '#' export let href: string = '#'
export let click: () => void = () => { export let click: () => void = () => {
goto(href) goto(href)
} }
const size = style === ButtonStyles.Micro ? 'sm' : 'lg'
</script> </script>
<button class={style} {disabled} on:click={click}> <slot /></button> <Button
class={style}
<style type="text/scss"> color={'primary'}
button, {disabled}
.button { block={style === ButtonStyles.Wide}
min-width: 100px; {size}
width: 100px; on:click={click}><slot /></Button
white-space: nowrap; >
border-radius: 5px;
color: white;
background: rgb(10, 10, 120);
display: inline-block;
margin-left: auto;
margin-right: auto;
padding: 10px;
font-size: 30px;
&:hover {
cursor: pointer;
text-decoration: none;
}
&:disabled {
background: rgb(64, 64, 107);
color: lightgray;
}
&.wide {
width: 100%;
}
&.micro {
width: initial;
padding: 2px;
padding-left: 5px;
padding-right: 5px;
min-width: initial;
max-width: initial;
font-size: 10px;
}
}
</style>

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { onMount, tick, createEventDispatcher } from 'svelte' import { createEventDispatcher, tick } from 'svelte'
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()

View File

@ -0,0 +1,32 @@
<script lang="ts">
import { Card, CardBody, CardHeader, CardText, CardTitle } from 'sveltestrap'
import Check from './Icons/Check.svelte'
export let title: string
</script>
<main>
<Card class="">
<CardHeader>
<CardTitle><Check /> {title}</CardTitle>
</CardHeader>
<CardBody style={'height: 220px'}>
<CardText>
<div class="body">
<slot />
</div>
</CardText>
</CardBody>
</Card>
</main>
<style lang="scss">
main {
width: 300px;
margin: 5px;
display: inline-block;
}
.body {
text-align: left;
}
</style>

View File

@ -0,0 +1,8 @@
<div class="gap" />
<style lang="scss">
.gap {
height: 10px;
width: 100%;
}
</style>

View File

@ -0,0 +1,5 @@
<script lang="ts">
import { Icon } from 'sveltestrap'
</script>
<Icon name="check" />

View File

@ -0,0 +1,54 @@
<script lang="ts">
import { redirect } from '$util/redirect'
import { isLoggedIn, logOut, user } from '@pockethost/common/src/pocketbase'
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'sveltestrap'
import logo from '../assets/logo-square.png'
import NavbarBrandImage from './NavbarBrandImage.svelte'
import Title from './Title/Title.svelte'
import { TitleSize } from './Title/types'
let isOpen = false
function handleUpdate(event: CustomEvent<boolean>) {
isOpen = event.detail.valueOf()
}
const handleLogout = () => {
logOut()
redirect(`/`)
}
</script>
<Navbar color="light" light expand="md">
<NavbarBrand href="/">
<NavbarBrandImage {logo} />
<Title size={TitleSize.Nav} first="pocket" second="host" third=".io" /></NavbarBrand
>
<NavbarToggler on:click={() => (isOpen = !isOpen)} />
<Collapse {isOpen} navbar expand="md" on:update={handleUpdate}>
<Nav class="ms-auto" navbar>
{#if isLoggedIn()}
<NavItem>
<div class="navbar-text">Welcome back, {user()?.email}</div>
</NavItem>
<NavItem>
<NavLink href="/dashboard">Dashboard</NavLink>
</NavItem>
<NavItem>
<NavLink on:click={handleLogout}>Logout</NavLink>
</NavItem>
{/if}
{#if !isLoggedIn()}
<NavItem>
<NavLink href="/signup">Sign up</NavLink>
</NavItem>
<NavItem>
<NavLink href="/login">Log in</NavLink>
</NavItem>
{/if}
</Nav>
</Collapse>
</Navbar>
<style lang="scss">
</style>

View File

@ -0,0 +1,12 @@
<script lang="ts">
export let logo: string
</script>
<img class="logo d-inline-block align-text-top" src={logo} />
<style lang="scss">
img.logo {
height: 30px;
margin-right: 10px;
}
</style>

View File

@ -1,31 +1,14 @@
<script lang="ts"> <script lang="ts">
import { isLoggedIn, logOut, user } from '@pockethost/common/src/pocketbase' import { browser } from '$app/environment'
import Button from './Button/Button.svelte' import { redirect } from '$util/redirect'
import { ButtonStyles } from './Button/types' import { isLoggedIn } from '@pockethost/common/src/pocketbase'
if (typeof window !== 'undefined' && !isLoggedIn()) { if (browser && !isLoggedIn()) {
window.location.href = '/signup' redirect(`/signup`)
}
const handleLogout = () => {
logOut()
window.location.href = '/'
} }
</script> </script>
<div class="logbar">
{user()?.email}
<Button style={ButtonStyles.Micro} click={handleLogout}>Log Out</Button>
<div>
<a href="/dashboard">dashboard</a>
</div>
</div>
<slot /> <slot />
<style lang="scss"> <style lang="scss">
.logbar {
position: absolute;
right: 0px;
top: 0px;
}
</style> </style>

View File

@ -1,10 +1,13 @@
<script lang="ts"> <script lang="ts">
import { TitleSize } from './types'
export let first = 'Pocket' export let first = 'Pocket'
export let second = 'Host' export let second = 'Host'
export let third = '' export let third = ''
export let size: TitleSize = TitleSize.Normal
</script> </script>
<h1>{first}<span id="host">{second}</span>{third}</h1> <h1 class={size}>{first}<span id="host">{second}</span>{third}</h1>
<style type="text/scss"> <style type="text/scss">
h1 { h1 {
@ -18,5 +21,10 @@
#host { #host {
color: blue; color: blue;
} }
&.nav {
font-size: 15px;
font-weight: 400;
display: inline-block;
}
} }
</style> </style>

View File

@ -0,0 +1,4 @@
export enum TitleSize {
Nav = 'nav',
Normal = 'normal'
}

View File

@ -0,0 +1,8 @@
<script>
import Navbar from '$components/Navbar.svelte'
import { Styles } from 'sveltestrap'
</script>
<Styles />
<Navbar />
<slot />

View File

@ -1,21 +1,78 @@
<script lang="ts"> <script lang="ts">
import Title from '../components/Title.svelte' import Button from '$components/Button/Button.svelte'
import Button from '../components/Button/Button.svelte' import { ButtonStyles } from '$components/Button/types'
import AuthCheck from '../components/AuthCheck.svelte' import Caption from '$components/Caption/Caption.svelte'
import Caption from '../components/Caption/Caption.svelte' import { CaptionSize } from '$components/Caption/types'
import { CaptionSize } from '../components/Caption/types' import Feature from '$components/Feature.svelte'
import Gap from '$components/Gap.svelte'
</script> </script>
<AuthCheck> <main>
<Title third=".io" /> <Caption size={CaptionSize.Hero}>Firebase and Supabase...<br />ljbf</Caption>
<main>
<Caption size={CaptionSize.Hero}> <div style="max-width: 400px; margin-left: auto; margin-right: auto">
The zero config, zero setup <a href="https://pocketbase.io">PocketBase</a> <Button href="/signup" style={ButtonStyles.Wide}>Get Started Free</Button>
backend for your next app. </div>
</Caption> <Gap />
<Button href="/signup">Get Started</Button> <div>
</main> <Feature title={`Up in 30 seconds`}>
</AuthCheck> <p>
A backend for your next app is as fast as signing up. No provisioning servers, no Docker
fiddling, just B(ad)aaS productivity.
</p>
<ul>
<li>Sign up</li>
<li>Pick a unique project name</li>
<li>Connect with our JS client</li>
</ul>
</Feature>
<Feature title={'Zero Config'}
>With PocketHost, batteries are included. You get a database, outgoing email, SSL,
authentication, cloud functions, and high concurrency all in one stop.</Feature
>
<Feature title={'Database'}>
Your PocketHost instance is powered by its own internal SQLite instance. SQLite is <a
href="https://pocketbase.io/faq/"
target="_blank">more performant than mySQL or Postgres</a
>
and is
<a href="https://www.sqlite.org/whentouse.html" target="_blank"
>perfect for powering your next app</a
>.
</Feature>
<Feature title={'Auth'}
>Email and oAuth authentication options work out of the box. Send transactional email to your
users from our verified domain and your custom address, <code>yoursubdomin@pockethost.io</code
>.</Feature
>
<Feature title={'Storage'}
>PocketHost securely stores your files on Amazon S3, or you can use your own key to manage
your own storage.</Feature
>
<Feature title={'Room to Grow'}
>PocketHost is perfect for hobbist, low, and medium volume sites and apps.
<p>
PocketHost, and the underlying PocketBase, can scale to well over 10,000 simultaneous
connections.
</p></Feature
>
<Feature title={`Self-host`}>
When you're ready to take your project in-house, we have you covered. You can export your
entire PocketHost environment along with a Dockerfile to run it.
</Feature>
<Feature title={'Open Source Stack'}
>PocketHost is powered by Svelte, Vite, Typescript, PocketBase, and SQLite. Because the entire
stack is open source, you'll never be locked into the whims of a vendor.</Feature
>
<Feature title={'Coming Soon'}>
<ul>
<li>JS/TS cloud functions</li>
<li>Deploy to Fly.io</li>
<li>Lightstream</li>
</ul>
</Feature>
</div>
</main>
<style type="text/scss"> <style type="text/scss">
main { main {

View File

@ -1,67 +1,66 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores' import { page } from '$app/stores'
import { identity } from 'ts-brand' import Caption from '$components/Caption/Caption.svelte'
import Caption from '../../../../components/Caption/Caption.svelte' import CodeSample from '$components/CodeSample.svelte'
import CodeSample from '../../../../components/CodeSample.svelte' import Protected from '$components/Protected.svelte'
import Protected from '../../../../components/Protected.svelte' import ProvisioningStatus from '$components/ProvisioningStatus/ProvisioningStatus.svelte'
import Title from '../../../../components/Title.svelte' import { ProvisioningSize } from '$components/ProvisioningStatus/types'
import { isBrowser } from '@pockethost/common/src/isBrowser' import Title from '$components/Title/Title.svelte'
import { assertExists } from '@pockethost/common/src/assert' import { assertExists } from '@pockethost/common/src/assert'
import { getInstanceById, watchInstanceById } from '@pockethost/common/src/pocketbase' import { watchInstanceById } from '@pockethost/common/src/pocketbase'
import { import {
InstanceStatuses, InstanceStatuses,
type InstanceId, type InstanceId,
type Instance_Out type Instance_Out
} from '@pockethost/common/src/schema' } from '@pockethost/common/src/schema'
import { onDestroy, onMount } from 'svelte' import { onDestroy, onMount } from 'svelte'
import type { Unsubscriber } from 'svelte/store' import type { Unsubscriber } from 'svelte/store'
import ProvisioningStatus from '../../../../components/ProvisioningStatus/ProvisioningStatus.svelte' import { identity } from 'ts-brand'
import { ProvisioningSize } from '../../../../components/ProvisioningStatus/types'
const { instanceId } = $page.params const { instanceId } = $page.params
let instance: Instance_Out | undefined let instance: Instance_Out | undefined
let url: string let url: string
let code: string = '' let code: string = ''
let unsub: Unsubscriber = () => {} let unsub: Unsubscriber = () => {}
onMount(() => { onMount(() => {
unsub = watchInstanceById(identity<InstanceId>(instanceId), (r) => { unsub = watchInstanceById(identity<InstanceId>(instanceId), (r) => {
console.log(`got a record`, r) console.log(`got a record`, r)
instance = r instance = r
assertExists(instance, `Expected instance here`) assertExists(instance, `Expected instance here`)
const { subdomain } = instance const { subdomain } = instance
url = `https://${subdomain}.pockethost.io` url = `https://${subdomain}.pockethost.io`
code = `const url = '${url}'\nconst client = new PocketBase(url)` code = `const url = '${url}'\nconst client = new PocketBase(url)`
}) })
}) })
onDestroy(() => unsub()) onDestroy(() => unsub())
</script> </script>
<Protected> <Protected>
<Title /> <Title />
{#if instance} {#if instance}
{#if instance.status === InstanceStatuses.Started} {#if instance.status === InstanceStatuses.Started}
<Caption>Your PocketHost instance is now live.</Caption> <Caption>Your PocketHost instance is now live.</Caption>
<div> <div>
Admin URL: <a href={`${url}/_`} target="_blank">{`${url}/_`}</a> Admin URL: <a href={`${url}/_`} target="_blank">{`${url}/_`}</a>
</div> </div>
<div> <div>
JavaScript: JavaScript:
<CodeSample {code} /> <CodeSample {code} />
</div> </div>
{/if} {/if}
{#if instance.status !== InstanceStatuses.Started} {#if instance.status !== InstanceStatuses.Started}
<Caption>Please stand by, your instance is starting now...</Caption> <Caption>Please stand by, your instance is starting now...</Caption>
<div class="provisioning"> <div class="provisioning">
<ProvisioningStatus status={instance.status} size={ProvisioningSize.Hero} /> <ProvisioningStatus status={instance.status} size={ProvisioningSize.Hero} />
</div> </div>
{/if} {/if}
{/if} {/if}
</Protected> </Protected>
<style lang="scss"> <style lang="scss">
.provisioning { .provisioning {
text-align: center; text-align: center;
} }
</style> </style>

View File

@ -1,95 +1,96 @@
<script lang="ts"> <script lang="ts">
import { faRefresh } from '@fortawesome/free-solid-svg-icons' import { browser } from '$app/environment'
import PocketBase from 'pocketbase' import Button from '$components/Button/Button.svelte'
import { generateSlug } from 'random-word-slugs' import { ButtonStyles } from '$components/Button/types'
import Fa from 'svelte-fa/src/fa.svelte' import Error from '$components/Error/Error.svelte'
import Button from '../../../components/Button/Button.svelte' import { parseError } from '$components/Error/parseError'
import { ButtonStyles } from '../../../components/Button/types' import Protected from '$components/Protected.svelte'
import Error from '../../../components/Error/Error.svelte' import Title from '$components/Title/Title.svelte'
import { parseError } from '../../../components/Error/parseError' import { redirect } from '$util/redirect'
import Protected from '../../../components/Protected.svelte' import { faRefresh } from '@fortawesome/free-solid-svg-icons'
import Title from '../../../components/Title.svelte' import { assertExists } from '@pockethost/common/src/assert'
import { createInstance, user } from '@pockethost/common/src/pocketbase' import { createInstance, user } from '@pockethost/common/src/pocketbase'
import { redirect } from '../../../util/redirect' import type { Subdomain, UserId } from '@pockethost/common/src/schema'
import { identity } from 'ts-brand' import PocketBase from 'pocketbase'
import { assertExists } from '@pockethost/common/src/assert' import { generateSlug } from 'random-word-slugs'
import type { Subdomain, UserId } from '@pockethost/common/src/schema' import Fa from 'svelte-fa'
import { identity } from 'ts-brand'
const client = new PocketBase('https://db.pockethost.io') const client = new PocketBase('https://db.pockethost.io')
if (!client.authStore.isValid && typeof window !== 'undefined') { if (browser && !client.authStore.isValid) {
window.location.href = '/signup' redirect('/signup')
} }
let instanceName = generateSlug(2) let instanceName = generateSlug(2)
let errorMessage = '' let errorMessage = ''
let code = '' let code = ''
$: { $: {
code = `const url = 'https://${instanceName}.pockethost.io'\nconst client = new PocketBase(url)` code = `const url = 'https://${instanceName}.pockethost.io'\nconst client = new PocketBase(url)`
} }
const handleCreate = () => { const handleCreate = () => {
console.log(`creating `, instanceName) console.log(`creating `, instanceName)
const { id } = user() || {} const { id } = user() || {}
assertExists<UserId>(id, `Expected uid here`) assertExists<UserId>(id, `Expected uid here`)
createInstance({ createInstance({
subdomain: identity<Subdomain>(instanceName), subdomain: identity<Subdomain>(instanceName),
uid: id uid: id
}) })
.then((rec) => { .then((rec) => {
console.log(`Record`, rec) console.log(`Record`, rec)
redirect(`/app/instances/${rec.id}`) redirect(`/app/instances/${rec.id}`)
}) })
.catch((e) => { .catch((e) => {
errorMessage = parseError(e) errorMessage = parseError(e)
console.error(errorMessage, e) console.error(errorMessage, e)
}) })
} }
</script> </script>
<Protected> <Protected>
<Title /> <Title />
<main> <main>
<h2>New App</h2> <h2>New App</h2>
<div class="caption">Choose a name for your PocketBase app.</div> <div class="caption">Choose a name for your PocketBase app.</div>
<div class="subdomain"> <div class="subdomain">
<label for="instanceName">Instance Name</label> <label for="instanceName">Instance Name</label>
<Button click={() => (instanceName = generateSlug(2))} style={ButtonStyles.Micro}> <Button click={() => (instanceName = generateSlug(2))} style={ButtonStyles.Micro}>
<Fa icon={faRefresh} /></Button <Fa icon={faRefresh} /></Button
> >
<input <input
class="subdomain" class="subdomain"
name="instanceName" name="instanceName"
type="text" type="text"
bind:value={instanceName} bind:value={instanceName}
/>.pockethost.io />.pockethost.io
</div> </div>
<Error>{errorMessage}</Error> <Error>{errorMessage}</Error>
<Button click={handleCreate}>Create</Button> <Button click={handleCreate}>Create</Button>
</main> </main>
</Protected> </Protected>
<style type="text/scss"> <style type="text/scss">
main { main {
padding: 1em; padding: 1em;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
label { label {
display: block; display: block;
font-weight: bold; font-weight: bold;
width: 200px; width: 200px;
} }
.caption { .caption {
font-size: 15px; font-size: 15px;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
.subdomain { .subdomain {
input { input {
text-align: right; text-align: right;
max-width: 200px; max-width: 200px;
} }
white-space: nowrap; white-space: nowrap;
} }
} }
</style> </style>

View File

@ -1,57 +1,57 @@
<script lang="ts"> <script lang="ts">
import { getAllInstancesById } from '@pockethost/common/src/pocketbase' import Button from '$components/Button/Button.svelte'
import { InstanceStatuses, type Instance_Out_ByIdCollection } from '@pockethost/common/src/schema' import { ButtonStyles } from '$components/Button/types'
import { values } from '@s-libs/micro-dash' import Protected from '$components/Protected.svelte'
import Button from '../../components/Button/Button.svelte' import ProvisioningStatus from '$components/ProvisioningStatus/ProvisioningStatus.svelte'
import { ButtonStyles } from '../../components/Button/types' import Title from '$components/Title/Title.svelte'
import Protected from '../../components/Protected.svelte' import { getAllInstancesById } from '@pockethost/common/src/pocketbase'
import Title from '../../components/Title.svelte' import { InstanceStatuses, type Instance_Out_ByIdCollection } from '@pockethost/common/src/schema'
import ProvisioningStatus from '../../components/ProvisioningStatus/ProvisioningStatus.svelte' import { values } from '@s-libs/micro-dash'
let apps: Instance_Out_ByIdCollection = {} let apps: Instance_Out_ByIdCollection = {}
getAllInstancesById() getAllInstancesById()
.then((instances) => { .then((instances) => {
apps = instances apps = instances
}) })
.catch((e) => { .catch((e) => {
console.error(`Failed to fetch instances`) console.error(`Failed to fetch instances`)
}) })
</script> </script>
<Protected> <Protected>
<Title /> <Title />
<main> <main>
<h2>Dashboard</h2> <h2>Dashboard</h2>
<h4>Apps</h4> <h4>Apps</h4>
{#each values(apps) as app} {#each values(apps) as app}
<div> <div>
<ProvisioningStatus status={app.status} /> <ProvisioningStatus status={app.status} />
{app.subdomain}.pockethost.io {app.subdomain}.pockethost.io
<Button style={ButtonStyles.Micro} href={`/app/instances/${app.id}`}>Details</Button> <Button style={ButtonStyles.Micro} href={`/app/instances/${app.id}`}>Details</Button>
<Button <Button
disabled={app.status !== InstanceStatuses.Started} disabled={app.status !== InstanceStatuses.Started}
style={ButtonStyles.Micro} style={ButtonStyles.Micro}
click={() => { click={() => {
window.open(`https://${app.subdomain}.pockethost.io/_`) window.open(`https://${app.subdomain}.pockethost.io/_`)
}}>Admin</Button }}>Admin</Button
> >
</div> </div>
{/each} {/each}
<Button href="/app/new">+</Button> <Button href="/app/new">+</Button>
</main> </main>
</Protected> </Protected>
<style type="text/scss"> <style type="text/scss">
main { main {
padding: 1em; padding: 1em;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
.caption { .caption {
font-size: 30px; font-size: 30px;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
} }
</style> </style>

View File

@ -1,65 +1,64 @@
<script lang="ts"> <script lang="ts">
import { ClientResponseError } from 'pocketbase' import Button from '$components/Button/Button.svelte'
import AuthCheck from '../../components/AuthCheck.svelte' import Title from '$components/Title/Title.svelte'
import Button from '../../components/Button/Button.svelte' import { redirect } from '$util/redirect'
import Title from '../../components/Title.svelte' import { authViaEmail } from '@pockethost/common/src/pocketbase'
import { authViaEmail, createUser } from '@pockethost/common/src/pocketbase'
let email = '' let email = ''
let password = '' let password = ''
let loginError = '' let loginError = ''
const handleLogin = () => { const handleLogin = () => {
loginError = '' loginError = ''
authViaEmail(email, password) authViaEmail(email, password)
.then((user) => { .then((user) => {
console.log(user) console.log(user)
window.location.href = '/dashboard' redirect('/dashboard')
}) })
.catch((e) => { .catch((e) => {
loginError = e.message loginError = e.message
}) })
} }
</script> </script>
<Title first="Log" second="In" /> <Title first="Log" second="In" />
<main> <main>
<error>{loginError}</error> <error>{loginError}</error>
<div> <div>
<label for="email">Email</label> <label for="email">Email</label>
<input name="email" type="email" bind:value={email} /> <input name="email" type="email" bind:value={email} />
</div> </div>
<div> <div>
<label for="password">Password</label> <label for="password">Password</label>
<input name="password" type="password" bind:value={password} /> <input name="password" type="password" bind:value={password} />
</div> </div>
<div> <div>
Need to <a href="/signup">create an account</a>? Need to <a href="/signup">create an account</a>?
</div> </div>
<Button click={handleLogin} disabled={email.length === 0 || password.length === 0}>Log In</Button> <Button click={handleLogin} disabled={email.length === 0 || password.length === 0}>Log In</Button>
</main> </main>
<style type="text/scss"> <style type="text/scss">
error { error {
color: red; color: red;
display: block; display: block;
} }
label { label {
display: block; display: block;
font-weight: bold; font-weight: bold;
width: 200px; width: 200px;
} }
main { main {
padding: 1em; padding: 1em;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
.caption { .caption {
font-size: 30px; font-size: 30px;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
</style> </style>

View File

@ -1,84 +1,85 @@
<script lang="ts"> <script lang="ts">
import { authViaEmail, createUser } from '@pockethost/common/src/pocketbase'; import Button from '$components/Button/Button.svelte'
import Button from '../../components/Button/Button.svelte'; import { parseError } from '$components/Error/parseError'
import { parseError } from '../../components/Error/parseError'; import Title from '$components/Title/Title.svelte'
import Title from '../../components/Title.svelte'; import { redirect } from '$util/redirect'
import { authViaEmail, createUser } from '@pockethost/common/src/pocketbase'
let email = ''; let email = ''
let errorMessage = ''; let errorMessage = ''
let password = ''; let password = ''
let passwordError = ''; let passwordError = ''
// client.users // client.users
// .authViaEmail('ben@benallfree.com', 'Dhjb2X6C1y0W') // .authViaEmail('ben@benallfree.com', 'Dhjb2X6C1y0W')
// .then((u) => { // .then((u) => {
// console.log(`user logged in`, u) // console.log(`user logged in`, u)
// window.location.href = '/dashboard' // window.location.href = '/dashboard'
// }) // })
// .catch((e) => console.error(`user login error`, e)) // .catch((e) => console.error(`user login error`, e))
const handleSignup = () => { const handleSignup = () => {
errorMessage = ''; errorMessage = ''
passwordError = ''; passwordError = ''
createUser(email, password) createUser(email, password)
.then((user) => { .then((user) => {
console.log({ user }); console.log({ user })
authViaEmail(email, password) authViaEmail(email, password)
.then((u) => { .then((u) => {
console.log(`user logged in`, u); console.log(`user logged in`, u)
window.location.href = '/dashboard'; redirect('/dashboard')
}) })
.catch((e) => console.error(`user login error`, e)); .catch((e) => console.error(`user login error`, e))
}) })
.catch((e) => { .catch((e) => {
errorMessage = parseError(e); errorMessage = parseError(e)
console.error(errorMessage, e); console.error(errorMessage, e)
}); })
}; }
</script> </script>
<Title first="Sign" second="Up" /> <Title first="Sign" second="Up" />
<main> <main>
<div> <div>
<label for="email">Email</label> <label for="email">Email</label>
<input name="email" type="email" bind:value={email} /> <input name="email" type="email" bind:value={email} />
<error>{errorMessage}</error> <error>{errorMessage}</error>
</div> </div>
<div> <div>
<label for="password">Password</label> <label for="password">Password</label>
<input name="password" type="password" bind:value={password} /> <input name="password" type="password" bind:value={password} />
<error>{passwordError}</error> <error>{passwordError}</error>
</div> </div>
<Button click={handleSignup} disabled={email.length === 0 || password.length === 0}> <Button click={handleSignup} disabled={email.length === 0 || password.length === 0}>
Sign Up Sign Up
</Button> </Button>
<div> <div>
Already have an account? <a href="/login">Log in</a> Already have an account? <a href="/login">Log in</a>
</div> </div>
</main> </main>
<style type="text/scss"> <style type="text/scss">
error { error {
color: red; color: red;
display: block; display: block;
} }
label { label {
display: block; display: block;
font-weight: bold; font-weight: bold;
width: 200px; width: 200px;
} }
main { main {
padding: 1em; padding: 1em;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
.caption { .caption {
font-size: 30px; font-size: 30px;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
</style> </style>

View File

@ -800,6 +800,11 @@
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==
"@popperjs/core@^2.9.2":
version "2.11.6"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
"@rollup/pluginutils@^4.2.1": "@rollup/pluginutils@^4.2.1":
version "4.2.1" version "4.2.1"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
@ -2586,6 +2591,13 @@ svelte@^3.44.0:
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.50.1.tgz#b35fbc5e79ddd71e8bb27c3149ee6ff903841239" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.50.1.tgz#b35fbc5e79ddd71e8bb27c3149ee6ff903841239"
integrity sha512-bS4odcsdj5D5jEg6riZuMg5NKelzPtmsCbD9RG+8umU03TeNkdWnP6pqbCm0s8UQNBkqk29w/Bdubn3C+HWSwA== integrity sha512-bS4odcsdj5D5jEg6riZuMg5NKelzPtmsCbD9RG+8umU03TeNkdWnP6pqbCm0s8UQNBkqk29w/Bdubn3C+HWSwA==
sveltestrap@^5.9.0:
version "5.9.0"
resolved "https://registry.yarnpkg.com/sveltestrap/-/sveltestrap-5.9.0.tgz#69cffd4d1178d69da423b097a34d185294b9e1c0"
integrity sha512-ZSiYKYrKhDMhhbamnAFK3RK/uqUdcLgjae5Fk3GYdv6Ccth0tN2y6vSg+Vp/PBTYc51u08ZwnYvt8SfWSRNCMA==
dependencies:
"@popperjs/core" "^2.9.2"
svgo@^2.4.0: svgo@^2.4.0:
version "2.8.0" version "2.8.0"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"