some more routes and i18n stuff
This commit is contained in:
parent
638ed31b09
commit
0c1e843f08
|
@ -15,8 +15,25 @@
|
||||||
"subtitle": "We'll send you an email with a \"magic link\".",
|
"subtitle": "We'll send you an email with a \"magic link\".",
|
||||||
"prompt": "What's your email?",
|
"prompt": "What's your email?",
|
||||||
"actionButtonText": "Log in",
|
"actionButtonText": "Log in",
|
||||||
|
"createaccount": "create an account",
|
||||||
"apierror": {
|
"apierror": {
|
||||||
"authorization was provided but it is expired or invalid": "User does not exist, maybe consider creating an account?"
|
"authorization was provided but it is expired or invalid": "User does not exist, maybe consider creating an account?",
|
||||||
|
"xhrerror": "unable to contact server, please try again later"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"signup": {
|
||||||
|
"title": "Get started with trifid",
|
||||||
|
"subtitle": "Simply manage connectivity between hosts running any major OS.",
|
||||||
|
"prompt": "What is your email address?",
|
||||||
|
"actionButtonText": "Create account",
|
||||||
|
"login": "Already have an account?",
|
||||||
|
"sentMagicLink": "Check your email!",
|
||||||
|
"magicLinkExplainer": "We sent you a link, click on it to finish creating your account."
|
||||||
|
},
|
||||||
|
|
||||||
|
"magiclink": {
|
||||||
|
"loadtitle": "Verifying magic link...",
|
||||||
|
"loadsubtitle": "Hang on while we check this link."
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,12 +15,19 @@
|
||||||
isloading = true;
|
isloading = true;
|
||||||
|
|
||||||
let xhr = new XMLHttpRequest();
|
let xhr = new XMLHttpRequest();
|
||||||
|
xhr.timeout = 10000;
|
||||||
xhr.open('POST', `${API_ROOT}/v1/auth/magic-link`);
|
xhr.open('POST', `${API_ROOT}/v1/auth/magic-link`);
|
||||||
xhr.setRequestHeader("Content-Type", "application/json");
|
xhr.setRequestHeader("Content-Type", "application/json");
|
||||||
xhr.send(JSON.stringify({
|
xhr.send(JSON.stringify({
|
||||||
email: email
|
email: email
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
xhr.ontimeout = () => {
|
||||||
|
hasError = true;
|
||||||
|
error = t('login.apierror.timeout');
|
||||||
|
isloading = false;
|
||||||
|
};
|
||||||
|
|
||||||
xhr.onload = () => {
|
xhr.onload = () => {
|
||||||
if (xhr.status != 200) {
|
if (xhr.status != 200) {
|
||||||
// error
|
// error
|
||||||
|
@ -35,6 +42,12 @@
|
||||||
isFinished = true;
|
isFinished = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
xhr.onerror = () => {
|
||||||
|
hasError = true;
|
||||||
|
error = t('login.apierror.xhrerror');
|
||||||
|
isloading = false;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -71,6 +84,10 @@
|
||||||
</svg>
|
</svg>
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div class="text-xs mt-0.5">
|
||||||
|
<a class="font-bold text-purple-400 dark:text-purple-600" href="/signup">{t('login.createaccount')}</a>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import {t} from "$lib/i18n";
|
||||||
|
import {API_ROOT} from "$lib/config";
|
||||||
|
|
||||||
|
let isLoading = true;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex in-h-full items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="w-full max-w-md">
|
||||||
|
{#if isLoading}
|
||||||
|
<div>
|
||||||
|
<h1 class="font-semibold text-2xl">{t('magiclink.loadtitle')}</h1>
|
||||||
|
<h2 class="ftext-sm">{t('magiclink.loadsubtitle')}</h2>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,101 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import {t} from "$lib/i18n";
|
||||||
|
import {API_ROOT} from "$lib/config";
|
||||||
|
|
||||||
|
let email = "";
|
||||||
|
let isloading = false;
|
||||||
|
let isFinished = false;
|
||||||
|
let hasError = false;
|
||||||
|
let error = "";
|
||||||
|
|
||||||
|
function createAcc() {
|
||||||
|
if (isloading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isloading = true;
|
||||||
|
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
xhr.timeout = 10000;
|
||||||
|
xhr.open('POST', `${API_ROOT}/v1/signup`);
|
||||||
|
xhr.setRequestHeader("Content-Type", "application/json");
|
||||||
|
xhr.send(JSON.stringify({
|
||||||
|
email: email
|
||||||
|
}));
|
||||||
|
|
||||||
|
xhr.ontimeout = () => {
|
||||||
|
hasError = true;
|
||||||
|
error = t('signup.apierror.timeout');
|
||||||
|
isloading = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (xhr.status != 200) {
|
||||||
|
// error
|
||||||
|
hasError = true;
|
||||||
|
const rawerror = JSON.parse(xhr.responseText).errors[0].message;
|
||||||
|
|
||||||
|
error = t(`signup.apierror.${rawerror}`);
|
||||||
|
|
||||||
|
isloading = false;
|
||||||
|
} else {
|
||||||
|
isloading = false;
|
||||||
|
isFinished = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = () => {
|
||||||
|
hasError = true;
|
||||||
|
error = t('signup.apierror.xhrerror');
|
||||||
|
isloading = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex in-h-full items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="w-full max-w-md">
|
||||||
|
{#if !isFinished}
|
||||||
|
<!-- Title -->
|
||||||
|
<div>
|
||||||
|
<h1 class="font-semibold text-2xl">{t('signup.title')}</h1>
|
||||||
|
<h2 class="ftext-sm">{t('signup.subtitle')}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- The actual form -->
|
||||||
|
<form class="mt-5" action="#" method="POST" on:submit|preventDefault={createAcc}>
|
||||||
|
<div class="-space-y-px rounded-md shadow-sm">
|
||||||
|
<label for="email" class="sr-only">{t('signup.prompt')}</label>
|
||||||
|
<input bind:value={email} id="email"
|
||||||
|
class="dark:bg-slate-500 bg-gray-200 w-full rounded px-3 py-2 focus:outline-none focus:ring-purple-500 appearance-none">
|
||||||
|
{#if hasError}
|
||||||
|
<span class="text-red-600 text-sm">{error}</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="bg-purple-400 dark:bg-purple-600 mt-4 w-full py-2 -space-y-px rounded-md shadow-sm place-content-center">
|
||||||
|
{#if !isloading}
|
||||||
|
{t('signup.actionButtonText')}
|
||||||
|
{:else}
|
||||||
|
<svg class="animate-spin w-5 h-5 inline-block m-auto" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none" viewBox="0 0 24 24">
|
||||||
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
|
||||||
|
stroke-width="4"></circle>
|
||||||
|
<path class="opacity-75" fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="text-xs mt-0.5">
|
||||||
|
<a class="font-bold text-purple-400 dark:text-purple-600" href="/auth/login">{t('signup.login')}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
{:else}
|
||||||
|
<!-- Title -->
|
||||||
|
<div>
|
||||||
|
<h1 class="font-semibold text-2xl">{t('signup.sentMagicLink')}</h1>
|
||||||
|
<h2 class="ftext-sm">{t('signup.magicLinkExplainer')}</h2>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
Reference in New Issue