diff options
| author | Adam Soltys <[email protected]> | 2024-11-26 22:35:28 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-11-26 22:35:28 -0800 |
| commit | f99bd15fabd6d7b361794f756a69d6f05a50701f (patch) | |
| tree | 9bb1b14519b34c343ad3bcd001fbd4681a53e447 /src | |
| parent | f4eb6547d0db5edd0137fdefe461433500515040 (diff) | |
| download | coinos-ui-f99bd15fabd6d7b361794f756a69d6f05a50701f.tar.gz coinos-ui-f99bd15fabd6d7b361794f756a69d6f05a50701f.zip | |
warnings
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/Account.svelte | 16 | ||||
| -rw-r--r-- | src/components/AppHeader.svelte | 8 | ||||
| -rw-r--r-- | src/components/Code.svelte | 29 | ||||
| -rw-r--r-- | src/components/PasswordInput.svelte | 19 | ||||
| -rw-r--r-- | src/components/Pin.svelte | 9 | ||||
| -rw-r--r-- | src/locales/en.json | 13 | ||||
| -rw-r--r-- | src/routes/(app)/forgot/+page.svelte | 77 | ||||
| -rw-r--r-- | src/routes/(static)/docs/+page.svelte | 37 | ||||
| -rw-r--r-- | src/routes/(static)/forgot/+page.server.ts (renamed from src/routes/(app)/forgot/+page.server.ts) | 0 | ||||
| -rw-r--r-- | src/routes/(static)/forgot/+page.svelte | 66 | ||||
| -rw-r--r-- | src/routes/(static)/login/+page.svelte | 38 | ||||
| -rw-r--r-- | src/routes/(static)/register/+page.svelte | 50 |
12 files changed, 154 insertions, 208 deletions
diff --git a/src/components/Account.svelte b/src/components/Account.svelte index 4db77066..e075b130 100644 --- a/src/components/Account.svelte +++ b/src/components/Account.svelte @@ -21,17 +21,25 @@ class="block space-y-2 border-primary py-4 cursor-pointer" class:border-b-8={!last} onclick={go} + aria-label="Payments" > <div class="flex"> <Balance {balance} {user} {rate} /> - <a href={`/payments`} class="contents" onclick={setAccount}> + <a + href={`/payments`} + class="contents" + onclick={setAccount} + aria-label="Payments" + > <button class="ml-auto flex gap-1 mb-auto pb-4 pl-4"> - <iconify-icon icon="ph:clock-bold" width="32"></iconify-icon> + <iconify-icon icon="ph:clock-bold" width="32" aria-label="Payments" + ></iconify-icon> </button> </a> - <a href={`/account/${id}`} class="contents"> + <a href={`/account/${id}`} class="contents" aria-label="Settings"> <button class="flex gap-1 mb-auto pb-4 pl-4"> - <iconify-icon icon="ph:gear-bold" width="32"></iconify-icon> + <iconify-icon icon="ph:gear-bold" width="32" aria-label="Settings" + ></iconify-icon> </button> </a> </div> diff --git a/src/components/AppHeader.svelte b/src/components/AppHeader.svelte index 495e855b..39f97d4e 100644 --- a/src/components/AppHeader.svelte +++ b/src/components/AppHeader.svelte @@ -27,19 +27,23 @@ { href: `/${user?.username}`, icon: "ph:house-bold", + label: "Home", }, { href: `/${user?.username}/receive`, icon: "ph:hand-coins-bold", flip: "horizontal", + label: "Receive", }, { href: `/payments`, icon: "ph:clock-bold", + label: "Payments", }, { href: `/send`, icon: "ph:paper-plane-right-bold", + label: "Send", }, ]; @@ -59,8 +63,8 @@ > <nav class="flex justify-end items-center space-x-4 p-5"> {#if user} - {#each links as { href, icon, flip }} - <a {href} data-sveltekit-preload-code="eager"> + {#each links as { href, icon, flip, label }} + <a {href} data-sveltekit-preload-code="eager" aria-label={label}> <button class="btn-menu {opacity(href)}"> <iconify-icon {icon} width={w > 640 ? 32 : 24} {flip} ></iconify-icon> diff --git a/src/components/Code.svelte b/src/components/Code.svelte index 52c6bcf2..1c25feb8 100644 --- a/src/components/Code.svelte +++ b/src/components/Code.svelte @@ -4,28 +4,19 @@ let { sample } = $props(); </script> +{#snippet cp(something)} + <button type="button" class="ml-auto" onclick={() => copy(something)}> + <iconify-icon icon="ph:copy-bold" width="42"></iconify-icon> + </button> +{/snippet} + <div class="bg-primary text-primary-content rounded-lg p-4 flex gap-4 whitespace-pre-wrap font-mono items-center" > - <div class="overflow-x-scroll max-w-full scrollbar-hide"> + <div + class="overflow-x-scroll max-w-full [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]" + > <div class="break-all min-w-[800px]">{samples[sample]}</div> </div> - <iconify-icon - icon="ph:copy-bold" - onclick={() => copy(samples[sample])} - width="42" - class="ml-auto cursor-pointer" - ></iconify-icon> + {@render cp(samples[sample])} </div> - -<style> - .scrollbar-hide::-webkit-scrollbar { - display: none; - } - - /* For IE, Edge and Firefox */ - .scrollbar-hide { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ - } -</style> diff --git a/src/components/PasswordInput.svelte b/src/components/PasswordInput.svelte index bac00134..eb5b0d6c 100644 --- a/src/components/PasswordInput.svelte +++ b/src/components/PasswordInput.svelte @@ -1,7 +1,11 @@ <script> - let { value = $bindable(), focus = () => {} } = $props(); + let { + show = $bindable(), + value = $bindable(), + focus = () => {}, + placeholder, + } = $props(); let toggle = (e) => (show = !show); - let show = $state(false); let icon = $derived(show ? "ph:eye-bold" : "ph:eye-slash-bold"); </script> @@ -10,9 +14,16 @@ class="input flex items-center justify-center gap-2 w-full" > {#if show} - <input name="password" type="text" bind:value class="clean" /> + <input name="password" type="text" bind:value class="clean" {placeholder} /> {:else} - <input name="password" type="password" bind:value class="clean" use:focus /> + <input + name="password" + type="password" + bind:value + class="clean" + use:focus + {placeholder} + /> {/if} <button type="button" aria-label="Toggle" onclick={toggle} class="contents"> <iconify-icon {icon} width="32"></iconify-icon> diff --git a/src/components/Pin.svelte b/src/components/Pin.svelte index fb17c612..a429e976 100644 --- a/src/components/Pin.svelte +++ b/src/components/Pin.svelte @@ -45,23 +45,22 @@ {#if loaded} <div + role="dialog" class="fixed bg-base-100 bg-opacity-90 inset-0 h-full w-full z-50 cursor-default" on:click|preventDefault + aria-labelledby="title" > <div class="mx-auto p-5 border shadow-lg rounded-md bg-base-100 space-y-5 max-w-lg" > - <h1 class="text-center text-2xl font-semibold">{title}</h1> + <h1 id="title" class="text-center text-2xl font-semibold">{title}</h1> <Pinpad bind:v={p} {cancel} /> <div> <label for="locktime" class="font-bold" >{$t("user.settings.rememberFor")}</label > - <select - name="locktime" - bind:value={locktime} - > + <select name="locktime" bind:value={locktime}> <option value={30}>30 {$t("user.settings.seconds")}</option> <option value={5 * 60}>5 {$t("user.settings.minutes")}</option> <option value={10 * 60}>10 {$t("user.settings.minutes")}</option> diff --git a/src/locales/en.json b/src/locales/en.json index c47bf051..10bccaba 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -146,7 +146,10 @@ }, "login": { "forgot": "Forgot password?", + "resetSent": "A password reset link will be sent to your email if we have it on file.", + "continue": "Continue", "reset": "Password reset", + "resetDesc": "We can send a reset link if you added and verified your email otherwise please reach out to", "optional": "Optional", "submit": "Submit", "token": "2FA Token", @@ -165,8 +168,8 @@ "failed": "Login failed" }, "nav": { - "dark": "Dark", - "light": "Light", + "dark": "Dark", + "light": "Light", "about": "About", "docs": "Docs", "messages": "Messages", @@ -176,7 +179,7 @@ "profile": "Profile", "register": "Register", "account": "Account", - "startInSeconds": "Start in Seconds", + "startInSeconds": "Start in Seconds", "signIn": "Sign In", "signOut": "Sign out", "dashboard": "Dashboard", @@ -244,7 +247,7 @@ "placeholder": "Enter an address, invoice or username" }, "settings": { - "pleaseEnterYourPassword": "Please enter your password", + "pleaseEnterYourPassword": "Please enter your password", "memoPrompt": "Checkout instructions", "memoPromptPlaceholder": "e.g. Please enter your name for the order", "rememberFor": "Remember for", @@ -359,7 +362,7 @@ "fundingAmount": "Funding amount", "fast": "Fast", "ecashDesc": "Ecash tokens are funded with bitcoin. They may be redeemed in exchange for a lightning payment equal to their value or loaded back into a coinos account at any time. They can also be transferred to other ecash wallets either wholly or partially. You might use them to give bitcoin to someone who doesn't have a wallet by texting the token to them or sharing a link to it. Learn more at", - "ecashToken": "Ecash token", + "ecashToken": "Ecash token", "medium": "Medium", "maxFeeDesc": "Set an upper limit on the network routing fee. If set too low the payment may fail. You can set it higher and try again. We debit the full amount while attempting the payment but credit back any unused portion.", "slow": "Slow", diff --git a/src/routes/(app)/forgot/+page.svelte b/src/routes/(app)/forgot/+page.svelte deleted file mode 100644 index eb981734..00000000 --- a/src/routes/(app)/forgot/+page.svelte +++ /dev/null @@ -1,77 +0,0 @@ -<script> - import { focus } from "$lib/utils"; - import { enhance } from "$app/forms"; - import { t } from "$lib/translations"; - import Icon from "$comp/Icon.svelte"; - import Spinner from "$comp/Spinner.svelte"; - import { fly } from "svelte/transition"; - - let { form } = $props(); - let email = $state(); - let loading; -</script> - -<div class="pt-10"> - <div class="w-[243px] mx-auto mb-10"> - <a href="/"> - <Icon icon="logo" /> - </a> - </div> - - <div class="flex justify-center items-center"> - <div - class="shadow-xl rounded-3xl p-10 pb-12 space-y-5 w-full mx-5 md:mx-0 md:w-[400px]" - > - <h1 class="text-2xl font-bold text-center">{$t("login.reset")}</h1> - - {#if form?.error} - <div class="text-red-600 text-center" in:fly> - {form.error} - </div> - {/if} - - {#if form?.success} - <div class="text-secondary"> - A password reset link will be sent to your email if we have it on - file. - </div> - - <div class="flex w-full"> - <a - href="/login" - class="bg-black text-white w-full rounded-2xl p-4 font-semibold hover:opacity-80 text-center" - > - Continue - </a> - </div> - {:else} - <form class="space-y-5" method="POST" use:enhance> - <div> - <label for="email" class="font-semibold">{$t("login.email")}</label> - <input - name="email" - type="text" - required - class="bg-primary" - bind:value={email} - use:focus - autocapitalize="none" - /> - </div> - - <button - type="submit" - class="bg-black text-white w-full rounded-2xl p-4 font-semibold hover:opacity-80" - disabled={loading} - > - {#if loading} - <Spinner /> - {:else} - {$t("login.submit")} - {/if} - </button> - </form> - {/if} - </div> - </div> -</div> diff --git a/src/routes/(static)/docs/+page.svelte b/src/routes/(static)/docs/+page.svelte index 80052805..f4e2ffb9 100644 --- a/src/routes/(static)/docs/+page.svelte +++ b/src/routes/(static)/docs/+page.svelte @@ -18,12 +18,17 @@ : `export token=<your auth token>`; </script> +{#snippet cp(something)} + <button type="button" class="ml-auto" onclick={() => copy(something)}> + <iconify-icon icon="ph:copy-bold" width="42"></iconify-icon> + </button> +{/snippet} + <div class="space-y-8 lg:text-xl mt-20 pt-20 md:pt-0 w-full max-w-full"> <h1 class="text-4xl">Documentation</h1> <p class="text-secondary"> - Coinos is free and open source software. The code is - available at <a + Coinos is free and open source software. The code is available at <a href="https://github.com/coinos" class="text-secondary underline">github.com/coinos</a > @@ -31,19 +36,18 @@ <p class="text-secondary"> Coinos has a REST API that can be used to register accounts and make - payments. The following examples show how you can call the API - with <a href="https://curl.se/" class="text-secondary underline">curl</a> + payments. The following examples show how you can call the API with <a + href="https://curl.se/" + class="text-secondary underline">curl</a + > </p> <h2 class="text-2xl">API Base URL</h2> - <div class="bg-primary text-primary-content rounded-lg p-4 flex gap-4 items-center"> + <div + class="bg-primary text-primary-content rounded-lg p-4 flex gap-4 items-center" + > <div>{api}</div> - <iconify-icon - icon="ph:copy-bold" - onclick={() => copy(api)} - width="42" - class="ml-auto cursor-pointer" - ></iconify-icon> + {@render cp(api)} </div> <h2 class="text-2xl">Auth Token</h2> @@ -58,14 +62,11 @@ Save it in a variable called <b>$token</b> to run the examples. </p> - <div class="bg-primary text-primary-content rounded-lg p-4 flex gap-4 items-center"> + <div + class="bg-primary text-primary-content rounded-lg p-4 flex gap-4 items-center" + > <div class="w-full break-all">{tokenSample}</div> - <iconify-icon - icon="ph:copy-bold" - onclick={() => copy(tokenSample)} - width="42" - class="ml-auto cursor-pointer" - ></iconify-icon> + {@render cp(tokenSample)} </div> <h2 class="text-2xl">POST /register</h2> diff --git a/src/routes/(app)/forgot/+page.server.ts b/src/routes/(static)/forgot/+page.server.ts index 55169a5e..55169a5e 100644 --- a/src/routes/(app)/forgot/+page.server.ts +++ b/src/routes/(static)/forgot/+page.server.ts diff --git a/src/routes/(static)/forgot/+page.svelte b/src/routes/(static)/forgot/+page.svelte new file mode 100644 index 00000000..45180543 --- /dev/null +++ b/src/routes/(static)/forgot/+page.svelte @@ -0,0 +1,66 @@ +<script> + import { focus } from "$lib/utils"; + import { enhance } from "$app/forms"; + import { t } from "$lib/translations"; + import Icon from "$comp/Icon.svelte"; + import Spinner from "$comp/Spinner.svelte"; + import { fly } from "svelte/transition"; + + let { form } = $props(); + let email = $state(); + let loading; +</script> + +<div + class="mx-auto md:shadow-xl rounded-3xl max-w-xl w-full md:w-[480px] md:p-8 mb-20 space-y-5" +> + <h1 class="text-2xl font-bold text-center">{$t("login.reset")}</h1> + + {#if form?.error} + <div class="text-red-600 text-center" in:fly> + {form.error} + </div> + {/if} + + {#if form?.success} + <div class="text-secondary"> + {$t("login.resetSent")} + </div> + + <div class="flex w-full"> + <a href="/login" class="btn"> + {$t("login.continue")} + </a> + </div> + {:else} + <form class="space-y-5" method="POST" use:enhance> + <div> + <input + placeholder={$t("login.email")} + name="email" + type="text" + required + class="bg-primary" + bind:value={email} + use:focus + autocapitalize="none" + /> + </div> + + <div class="text-secondary"> + {$t("login.resetDesc")} + <a href="mailto:[email protected]" class="font-bold" + >[email protected]</a + > + </div> + + <button type="submit" class="btn" disabled={loading}> + {#if loading} + <Spinner /> + {:else} + {$t("login.submit")} + {/if} + </button> + </form> + {/if} +</div> diff --git a/src/routes/(static)/login/+page.svelte b/src/routes/(static)/login/+page.svelte index 2b36dcfe..5caf48fb 100644 --- a/src/routes/(static)/login/+page.svelte +++ b/src/routes/(static)/login/+page.svelte @@ -8,6 +8,7 @@ import { fly } from "svelte/transition"; import { enhance } from "$app/forms"; import Pinpad from "$comp/Pinpad.svelte"; + import PasswordInput from "$comp/PasswordInput.svelte"; import { focus, fail } from "$lib/utils"; import { password, pin, loginRedirect } from "$lib/store"; import { t } from "$lib/translations"; @@ -105,38 +106,7 @@ placeholder={$t("login.username")} /> - <label - for="password" - class="input flex items-center justify-center gap-2 w-full" - > - {#if revealPassword} - <input - class="clean" - name="password" - type="text" - required - bind:value={$password} - autocapitalize="none" - placeholder={$t("login.password")} - /> - {:else} - <input - class="clean" - name="password" - type="password" - required - bind:value={$password} - autocapitalize="none" - placeholder={$t("login.password")} - /> - {/if} - <iconify-icon - class="cursor-pointer ml-auto" - onclick={() => (revealPassword = !revealPassword)} - icon={revealPassword ? "ph:eye-bold" : "ph:eye-slash-bold"} - width="32" - ></iconify-icon> - </label> + <PasswordInput bind:value={$password} placeholder={$t("login.password")} /> <div class="flex justify-end items-center"> <a href="/forgot" class="underline underline-offset-4 text-secondary" @@ -164,11 +134,13 @@ <div class="fixed bg-base-100 bg-opacity-90 inset-0 h-full w-full z-50 cursor-default" onclick={(e) => e.stopPropagation()} + role="dialog" + aria-labelledby="title" > <div class="mx-auto p-5 border shadow-lg rounded-md bg-base-100 space-y-5 max-w-lg" > - <h1 class="text-center text-2xl font-semibold">2FA</h1> + <h1 id="title" class="text-center text-2xl font-semibold">2FA</h1> <Pinpad bind:v={token} {cancel} /> <div class="w-full flex"> diff --git a/src/routes/(static)/register/+page.svelte b/src/routes/(static)/register/+page.svelte index 8a2a5015..147c361e 100644 --- a/src/routes/(static)/register/+page.svelte +++ b/src/routes/(static)/register/+page.svelte @@ -9,6 +9,7 @@ import Pin from "$comp/Pin.svelte"; import Spinner from "$comp/Spinner.svelte"; + import PasswordInput from "$comp/PasswordInput.svelte"; import { focus, fail } from "$lib/utils"; import { avatar, password, loginRedirect } from "$lib/store"; @@ -27,6 +28,7 @@ let username = $state(); let { index } = $state(data); + let revealPassword = $state(false); let cleared; let clear = () => { @@ -40,6 +42,8 @@ let refresh = async (e) => { e.preventDefault(); + cleared = false; + username = uniqueNamesGenerator({ dictionaries: [animals, NumberDictionary.generate({ min: 10, max: 99 })], length: 2, @@ -73,8 +77,6 @@ let email, btn = $state(); - let revealPassword = $state(); - let loading = $state(); async function handleSubmit(e) { e.preventDefault(); @@ -245,45 +247,11 @@ > </label> - <label - for="password" - class="input flex items-center justify-center gap-2 w-full" - > - {#if revealPassword} - <input - class="clean" - name="password" - type="text" - required - bind:value={$password} - autocapitalize="none" - placeholder={$t("login.password")} - /> - {:else} - <input - class="clean" - name="password" - type="password" - required - bind:value={$password} - autocapitalize="none" - onfocus={clear} - placeholder={$t("login.password")} - /> - {/if} - <button - type="button" - tabindex="-1" - onclick={() => (revealPassword = !revealPassword)} - class="contents" - aria-label="Toggle" - > - <iconify-icon - icon={revealPassword ? "ph:eye-bold" : "ph:eye-slash-bold"} - width="32" - ></iconify-icon></button - > - </label> + <PasswordInput + bind:value={$password} + placeholder={$t("login.password")} + bind:show={revealPassword} + /> <button type="submit" class="btn" disabled={loading} bind:this={btn}> {#if loading} |
