mirror of https://github.com/nocodb/nocodb
Raju Udava
2 years ago
committed by
GitHub
12 changed files with 555 additions and 340 deletions
@ -1,143 +0,0 @@ |
|||||||
<script setup lang="ts"> |
|
||||||
import { useI18n } from 'vue-i18n' |
|
||||||
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils' |
|
||||||
import { navigateTo } from '#app' |
|
||||||
import { isEmail } from '~/utils/validation' |
|
||||||
import MaterialSymbolsWarning from '~icons/material-symbols/warning' |
|
||||||
import MaterialSymbolsRocketLaunchOutline from '~icons/material-symbols/rocket-launch-outline' |
|
||||||
|
|
||||||
const { $api, $state } = useNuxtApp() |
|
||||||
|
|
||||||
const { t } = useI18n() |
|
||||||
|
|
||||||
definePageMeta({ |
|
||||||
requiresAuth: false, |
|
||||||
}) |
|
||||||
|
|
||||||
const formValidator = ref() |
|
||||||
let error = $ref<string | null>(null) |
|
||||||
|
|
||||||
const form = reactive({ |
|
||||||
email: '', |
|
||||||
password: '', |
|
||||||
}) |
|
||||||
|
|
||||||
const formRules = { |
|
||||||
email: [ |
|
||||||
// E-mail is required |
|
||||||
{ required: true, message: t('msg.error.signUpRules.emailReqd') }, |
|
||||||
// E-mail must be valid format |
|
||||||
{ |
|
||||||
validator: (_: unknown, v: string) => { |
|
||||||
return new Promise((resolve, reject) => { |
|
||||||
if (isEmail(v)) return resolve(true) |
|
||||||
reject(new Error(t('msg.error.signUpRules.emailInvalid'))) |
|
||||||
}) |
|
||||||
}, |
|
||||||
message: t('msg.error.signUpRules.emailInvalid'), |
|
||||||
}, |
|
||||||
], |
|
||||||
password: [ |
|
||||||
// Password is required |
|
||||||
{ required: true, message: t('msg.error.signUpRules.passwdRequired') }, |
|
||||||
{ min: 8, message: t('msg.error.signUpRules.passwdLength') }, |
|
||||||
], |
|
||||||
} |
|
||||||
|
|
||||||
const signUp = async () => { |
|
||||||
const valid = formValidator.value.validate() |
|
||||||
if (!valid) return |
|
||||||
|
|
||||||
error = null |
|
||||||
try { |
|
||||||
const { token } = await $api.auth.signup(form) |
|
||||||
$state.signIn(token!) |
|
||||||
await navigateTo('/') |
|
||||||
} catch (e: any) { |
|
||||||
error = await extractSdkResponseErrorMsg(e) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const resetError = () => { |
|
||||||
if (error) { |
|
||||||
error = null |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<template> |
|
||||||
<NuxtLayout> |
|
||||||
<a-form |
|
||||||
ref="formValidator" |
|
||||||
:model="form" |
|
||||||
layout="vertical" |
|
||||||
class="signup h-[calc(100%_+_90px)] min-h-[600px] flex justify-center items-center nc-form-signup" |
|
||||||
@finish="signUp" |
|
||||||
> |
|
||||||
<div class="h-full w-full flex flex-col flex-wrap pt-[100px]"> |
|
||||||
<div |
|
||||||
class="bg-white dark:(!bg-gray-900 !text-white) relative flex flex-col justify-center gap-2 w-full max-w-[500px] mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)" |
|
||||||
> |
|
||||||
<general-noco-icon /> |
|
||||||
|
|
||||||
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('general.signUp') }}</h1> |
|
||||||
|
|
||||||
<Transition name="layout"> |
|
||||||
<div v-if="error" class="self-center mb-4 bg-red-500 text-white rounded-lg w-3/4 p-1"> |
|
||||||
<div class="flex items-center gap-2 justify-center"><MaterialSymbolsWarning /> {{ error }}</div> |
|
||||||
</div> |
|
||||||
</Transition> |
|
||||||
|
|
||||||
<a-form-item :label="$t('labels.email')" name="email" :rules="formRules.email"> |
|
||||||
<a-input v-model:value="form.email" size="large" :placeholder="$t('labels.email')" @focus="resetError" /> |
|
||||||
</a-form-item> |
|
||||||
|
|
||||||
<a-form-item :label="$t('labels.password')" name="password" :rules="formRules.password"> |
|
||||||
<a-input-password |
|
||||||
v-model:value="form.password" |
|
||||||
size="large" |
|
||||||
class="password" |
|
||||||
:placeholder="$t('labels.password')" |
|
||||||
@focus="resetError" |
|
||||||
/> |
|
||||||
</a-form-item> |
|
||||||
|
|
||||||
<div |
|
||||||
class="self-center flex flex-column flex-wrap gap-4 items-center mt-4 md:mx-8 md:justify-between justify-center w-full" |
|
||||||
> |
|
||||||
<button class="submit" type="submit"> |
|
||||||
<span class="flex items-center gap-2"><MaterialSymbolsRocketLaunchOutline /> {{ $t('general.signUp') }}</span> |
|
||||||
</button> |
|
||||||
<div class="text-end prose-sm"> |
|
||||||
{{ $t('msg.info.signUp.alreadyHaveAccount') }} |
|
||||||
<nuxt-link to="/signin">{{ $t('general.signIn') }}</nuxt-link> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</a-form> |
|
||||||
</NuxtLayout> |
|
||||||
</template> |
|
||||||
|
|
||||||
<style lang="scss"> |
|
||||||
.signup { |
|
||||||
.ant-input-affix-wrapper, |
|
||||||
.ant-input { |
|
||||||
@apply dark:(bg-gray-700 !text-white) !appearance-none my-1 border-1 border-solid border-primary/50 rounded; |
|
||||||
} |
|
||||||
|
|
||||||
.password { |
|
||||||
input { |
|
||||||
@apply !border-none; |
|
||||||
} |
|
||||||
|
|
||||||
.ant-input-password-icon { |
|
||||||
@apply dark:!text-white; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
.submit { |
|
||||||
@apply ml-1 border border-gray-300 rounded-lg p-4 bg-gray-100/50 text-white bg-primary hover:bg-primary/75 dark:(!bg-secondary/75 hover:!bg-secondary/50); |
|
||||||
} |
|
||||||
} |
|
||||||
</style> |
|
@ -0,0 +1,206 @@ |
|||||||
|
<script setup lang="ts"> |
||||||
|
import { |
||||||
|
definePageMeta, |
||||||
|
extractSdkResponseErrorMsg, |
||||||
|
isEmail, |
||||||
|
navigateTo, |
||||||
|
reactive, |
||||||
|
ref, |
||||||
|
useApi, |
||||||
|
useGlobal, |
||||||
|
useI18n, |
||||||
|
useRoute, |
||||||
|
} from '#imports' |
||||||
|
|
||||||
|
definePageMeta({ |
||||||
|
requiresAuth: false, |
||||||
|
}) |
||||||
|
|
||||||
|
const route = useRoute() |
||||||
|
|
||||||
|
const { appInfo, signIn } = useGlobal() |
||||||
|
|
||||||
|
const { api, isLoading } = useApi() |
||||||
|
|
||||||
|
const { t } = useI18n() |
||||||
|
|
||||||
|
const formValidator = ref() |
||||||
|
|
||||||
|
let error = $ref<string | null>(null) |
||||||
|
|
||||||
|
const subscribe = ref(false) |
||||||
|
|
||||||
|
const form = reactive({ |
||||||
|
email: '', |
||||||
|
password: '', |
||||||
|
}) |
||||||
|
|
||||||
|
const formRules = { |
||||||
|
email: [ |
||||||
|
// E-mail is required |
||||||
|
{ required: true, message: t('msg.error.signUpRules.emailReqd') }, |
||||||
|
// E-mail must be valid format |
||||||
|
{ |
||||||
|
validator: (_: unknown, v: string) => { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
if (isEmail(v)) return resolve(true) |
||||||
|
reject(new Error(t('msg.error.signUpRules.emailInvalid'))) |
||||||
|
}) |
||||||
|
}, |
||||||
|
message: t('msg.error.signUpRules.emailInvalid'), |
||||||
|
}, |
||||||
|
], |
||||||
|
password: [ |
||||||
|
// Password is required |
||||||
|
{ required: true, message: t('msg.error.signUpRules.passwdRequired') }, |
||||||
|
{ min: 8, message: t('msg.error.signUpRules.passwdLength') }, |
||||||
|
], |
||||||
|
} |
||||||
|
|
||||||
|
async function signUp() { |
||||||
|
if (!formValidator.value.validate()) return |
||||||
|
|
||||||
|
resetError() |
||||||
|
|
||||||
|
const data: any = { |
||||||
|
...form, |
||||||
|
token: route.params.token, |
||||||
|
} |
||||||
|
|
||||||
|
if (subscribe.value) { |
||||||
|
data.ignore_subscribe = !subscribe.value |
||||||
|
} |
||||||
|
|
||||||
|
api.auth |
||||||
|
.signup(data) |
||||||
|
.then(async ({ token }) => { |
||||||
|
signIn(token!) |
||||||
|
|
||||||
|
await navigateTo('/') |
||||||
|
}) |
||||||
|
.catch(async (err) => { |
||||||
|
error = await extractSdkResponseErrorMsg(err) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
function resetError() { |
||||||
|
if (error) error = null |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<NuxtLayout> |
||||||
|
<div class="bg-primary/5 signup h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signup"> |
||||||
|
<div |
||||||
|
class="bg-white dark:(!bg-gray-900 !text-white) mt-[60px] relative flex flex-col justify-center gap-2 w-full max-w-[500px] mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)" |
||||||
|
> |
||||||
|
<general-noco-icon |
||||||
|
class="color-transition hover:(ring ring-pink-500)" |
||||||
|
:class="[isLoading ? 'animated-bg-gradient' : '']" |
||||||
|
/> |
||||||
|
|
||||||
|
<h1 class="prose-2xl font-bold self-center my-4"> |
||||||
|
{{ $t('general.signUp') }} |
||||||
|
{{ $route.query.redirect_to === '/referral' ? '& REFER' : '' }} |
||||||
|
{{ $route.query.redirect_to === '/pricing' ? '& BUY' : '' }} |
||||||
|
</h1> |
||||||
|
|
||||||
|
<h2 v-if="appInfo.firstUser" class="prose !text-primary font-semibold self-center my-4"> |
||||||
|
{{ $t('msg.info.signUp.superAdmin') }} |
||||||
|
</h2> |
||||||
|
|
||||||
|
<a-form ref="formValidator" :model="form" layout="vertical" no-style @finish="signUp"> |
||||||
|
<Transition name="layout"> |
||||||
|
<div v-if="error" class="self-center mb-4 bg-red-500 text-white rounded-lg w-3/4 mx-auto p-1"> |
||||||
|
<div class="flex items-center gap-2 justify-center"> |
||||||
|
<MaterialSymbolsWarning /> |
||||||
|
<div style="flex: 0 0 auto" class="break-words">{{ error }}</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Transition> |
||||||
|
|
||||||
|
<a-form-item :label="$t('labels.email')" name="email" :rules="formRules.email"> |
||||||
|
<a-input v-model:value="form.email" size="large" :placeholder="$t('labels.email')" @focus="resetError" /> |
||||||
|
</a-form-item> |
||||||
|
|
||||||
|
<a-form-item :label="$t('labels.password')" name="password" :rules="formRules.password"> |
||||||
|
<a-input-password |
||||||
|
v-model:value="form.password" |
||||||
|
size="large" |
||||||
|
class="password" |
||||||
|
:placeholder="$t('labels.password')" |
||||||
|
@focus="resetError" |
||||||
|
/> |
||||||
|
</a-form-item> |
||||||
|
|
||||||
|
<div class="self-center flex flex-col flex-wrap gap-4 items-center mt-4"> |
||||||
|
<button class="submit" type="submit"> |
||||||
|
<span class="flex items-center gap-2"> |
||||||
|
<MaterialSymbolsRocketLaunchOutline /> |
||||||
|
|
||||||
|
{{ $t('general.signUp') }} |
||||||
|
</span> |
||||||
|
</button> |
||||||
|
|
||||||
|
<div class="flex items-center gap-2"> |
||||||
|
<a-switch |
||||||
|
v-model:checked="subscribe" |
||||||
|
size="small" |
||||||
|
class="my-1 hover:(ring ring-pink-500) focus:(!ring !ring-pink-500)" |
||||||
|
/> |
||||||
|
<div class="prose-xs text-gray-500">Subscribe to our weekly newsletter</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="text-end prose-sm"> |
||||||
|
{{ $t('msg.info.signUp.alreadyHaveAccount') }} |
||||||
|
|
||||||
|
<nuxt-link to="/signin">{{ $t('general.signIn') }}</nuxt-link> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</a-form> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="prose-sm mt-4 text-gray-500"> |
||||||
|
By signing up, you agree to the |
||||||
|
<a class="prose-sm text-pink-500 underline" target="_blank" href="https://nocodb.com/policy-nocodb">Terms of Service</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</NuxtLayout> |
||||||
|
</template> |
||||||
|
|
||||||
|
<style lang="scss"> |
||||||
|
.signup { |
||||||
|
.ant-input-affix-wrapper, |
||||||
|
.ant-input { |
||||||
|
@apply dark:(bg-gray-700 !text-white) !appearance-none my-1 border-1 border-solid border-primary/50 rounded; |
||||||
|
} |
||||||
|
|
||||||
|
.password { |
||||||
|
input { |
||||||
|
@apply !border-none; |
||||||
|
} |
||||||
|
|
||||||
|
.ant-input-password-icon { |
||||||
|
@apply dark:!text-white; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.submit { |
||||||
|
@apply z-1 relative color-transition border border-gray-300 rounded-md p-3 bg-gray-100/50 text-white bg-primary; |
||||||
|
|
||||||
|
&::after { |
||||||
|
@apply rounded-md absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary; |
||||||
|
content: ''; |
||||||
|
z-index: -1; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover::after { |
||||||
|
@apply transform scale-110 ring ring-pink-500; |
||||||
|
} |
||||||
|
|
||||||
|
&:active::after { |
||||||
|
@apply ring ring-pink-500; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
Loading…
Reference in new issue