mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 03:55:59 +00:00
refactor: componentize auth pages
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
<template>
|
||||
<div class="create-account-card">
|
||||
<h1 class="create-account-title">{{ formatMessage(messages.title) }}</h1>
|
||||
|
||||
<section class="create-account-section">
|
||||
<label class="create-account-label" for="create-account-dob">
|
||||
{{ formatMessage(messages.dateOfBirthLabel) }}
|
||||
</label>
|
||||
<div class="date-input-wrap">
|
||||
<input
|
||||
id="create-account-dob"
|
||||
v-model="dateOfBirthModel"
|
||||
class="date-input"
|
||||
type="date"
|
||||
:max="maxBirthDate"
|
||||
/>
|
||||
<CalendarIcon class="date-input-icon" />
|
||||
</div>
|
||||
<p class="helper-text">{{ formatMessage(messages.over13HelperText) }}</p>
|
||||
</section>
|
||||
|
||||
<section class="info-panel">
|
||||
<div class="info-panel-icon">
|
||||
<InfoIcon />
|
||||
</div>
|
||||
<div class="info-panel-content">
|
||||
<p>{{ formatMessage(messages.infoPanelText) }}</p>
|
||||
<a class="text-link" :href="sourceCodeUrl" target="_blank" rel="noopener noreferrer">
|
||||
{{ formatMessage(messages.relevantSourceCodeText) }}
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="create-account-section">
|
||||
<label class="create-account-label" for="create-account-username">
|
||||
{{ formatMessage(messages.usernameOptionalLabel) }}
|
||||
</label>
|
||||
<StyledInput
|
||||
id="create-account-username"
|
||||
v-model="usernameModel"
|
||||
type="text"
|
||||
:placeholder="formatMessage(messages.usernamePlaceholder)"
|
||||
wrapper-class="w-full"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section class="create-account-section">
|
||||
<label class="create-account-label">{{ formatMessage(messages.securityCheckLabel) }}</label>
|
||||
<div class="captcha-wrap">
|
||||
<HCaptcha v-if="globals?.captcha_enabled" :ref="onSetCaptchaRef" v-model="tokenModel" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Checkbox
|
||||
v-model="subscribeModel"
|
||||
class="subscribe-checkbox"
|
||||
:label="formatMessage(messages.subscribeLabel)"
|
||||
:description="formatMessage(messages.subscribeLabel)"
|
||||
/>
|
||||
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
class="!w-full complete-sign-up-btn"
|
||||
:disabled="globals?.captcha_enabled ? !tokenModel : false"
|
||||
@click="onCompleteSignUp()"
|
||||
>
|
||||
{{ formatMessage(messages.completeSignUpButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { CalendarIcon, InfoIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, Checkbox, defineMessages, StyledInput, useVIntl } from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import HCaptcha from '@/components/ui/auth/HCaptcha.vue'
|
||||
|
||||
const props = defineProps({
|
||||
dateOfBirth: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
username: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
token: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
subscribe: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
globals: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
sourceCodeUrl: {
|
||||
type: String,
|
||||
default: 'https://github.com/modrinth/labrinth/blob/main/apps/labrinth/src/routes/internal/flows.rs',
|
||||
},
|
||||
onCompleteSignUp: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
onSetCaptchaRef: {
|
||||
type: Function,
|
||||
default: undefined,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:dateOfBirth',
|
||||
'update:username',
|
||||
'update:token',
|
||||
'update:subscribe',
|
||||
])
|
||||
|
||||
const dateOfBirthModel = computed({
|
||||
get: () => props.dateOfBirth,
|
||||
set: (value) => emit('update:dateOfBirth', value),
|
||||
})
|
||||
|
||||
const usernameModel = computed({
|
||||
get: () => props.username,
|
||||
set: (value) => emit('update:username', value),
|
||||
})
|
||||
|
||||
const tokenModel = computed({
|
||||
get: () => props.token,
|
||||
set: (value) => emit('update:token', value),
|
||||
})
|
||||
|
||||
const subscribeModel = computed({
|
||||
get: () => props.subscribe,
|
||||
set: (value) => emit('update:subscribe', value),
|
||||
})
|
||||
|
||||
const maxBirthDate = computed(() => {
|
||||
const date = new Date()
|
||||
date.setFullYear(date.getFullYear() - 13)
|
||||
return date.toISOString().slice(0, 10)
|
||||
})
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
title: {
|
||||
id: 'auth.create-account.title',
|
||||
defaultMessage: 'Create an Account',
|
||||
},
|
||||
dateOfBirthLabel: {
|
||||
id: 'auth.create-account.date-of-birth.label',
|
||||
defaultMessage: 'Date of birth',
|
||||
},
|
||||
over13HelperText: {
|
||||
id: 'auth.create-account.date-of-birth.over13-helper',
|
||||
defaultMessage: 'You must be over 13 years old to use Modrinth.',
|
||||
},
|
||||
infoPanelText: {
|
||||
id: 'auth.create-account.info-panel.text',
|
||||
defaultMessage: 'We do not store your date of birth, it is only used to confirm your age at sign up.',
|
||||
},
|
||||
relevantSourceCodeText: {
|
||||
id: 'auth.create-account.info-panel.source-code-link',
|
||||
defaultMessage: 'Relevant source code',
|
||||
},
|
||||
usernameOptionalLabel: {
|
||||
id: 'auth.create-account.username.optional-label',
|
||||
defaultMessage: 'Username (Optional)',
|
||||
},
|
||||
usernamePlaceholder: {
|
||||
id: 'auth.create-account.username.placeholder',
|
||||
defaultMessage: 'Enter username',
|
||||
},
|
||||
securityCheckLabel: {
|
||||
id: 'auth.create-account.security-check.label',
|
||||
defaultMessage: 'Security check',
|
||||
},
|
||||
subscribeLabel: {
|
||||
id: 'auth.create-account.subscribe.label',
|
||||
defaultMessage: 'Keep me updated on the cool things Modrinth is working on via email',
|
||||
},
|
||||
completeSignUpButton: {
|
||||
id: 'auth.create-account.complete-sign-up',
|
||||
defaultMessage: 'Complete sign up',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.create-account-card {
|
||||
background: var(--color-raised-bg);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border-radius: var(--size-rounded-xl);
|
||||
box-shadow: var(--shadow-card);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-md);
|
||||
margin-inline: auto;
|
||||
max-width: 30rem;
|
||||
padding: var(--gap-xl);
|
||||
}
|
||||
|
||||
.create-account-title {
|
||||
font-size: var(--text-4xl);
|
||||
font-weight: var(--weight-bold);
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.create-account-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-sm);
|
||||
}
|
||||
|
||||
.create-account-label {
|
||||
color: var(--color-contrast);
|
||||
font-size: var(--text-xl);
|
||||
font-weight: var(--weight-bold);
|
||||
}
|
||||
|
||||
.date-input-wrap {
|
||||
align-items: center;
|
||||
background: var(--color-button-bg);
|
||||
border-radius: var(--size-rounded-lg);
|
||||
display: flex;
|
||||
gap: var(--gap-sm);
|
||||
padding: 0.875rem 1rem;
|
||||
}
|
||||
|
||||
.date-input {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: var(--color-contrast);
|
||||
font-size: var(--text-lg);
|
||||
outline: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.date-input-icon {
|
||||
color: var(--color-secondary);
|
||||
flex-shrink: 0;
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
color: var(--color-secondary);
|
||||
font-size: var(--text-lg);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.info-panel {
|
||||
background: color-mix(in oklab, var(--color-brand) 20%, var(--color-bg));
|
||||
border: 1px solid color-mix(in oklab, var(--color-brand) 80%, transparent);
|
||||
border-radius: var(--size-rounded-xl);
|
||||
display: flex;
|
||||
gap: var(--gap-sm);
|
||||
padding: var(--gap-md);
|
||||
}
|
||||
|
||||
.info-panel-icon {
|
||||
color: var(--color-brand);
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-panel-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-xs);
|
||||
|
||||
p {
|
||||
color: var(--color-contrast);
|
||||
font-size: var(--text-lg);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.captcha-wrap {
|
||||
background: var(--color-button-bg);
|
||||
border-radius: var(--size-rounded-lg);
|
||||
min-height: 4.25rem;
|
||||
padding: var(--gap-md);
|
||||
}
|
||||
|
||||
.subscribe-checkbox {
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border-radius: var(--size-rounded-xl);
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.complete-sign-up-btn {
|
||||
font-weight: var(--weight-bold);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<div v-if="subtleLauncherRedirectUri">
|
||||
<iframe
|
||||
:src="subtleLauncherRedirectUri"
|
||||
class="fixed left-0 top-0 z-[9999] m-0 h-full w-full border-0 p-0"
|
||||
></iframe>
|
||||
</div>
|
||||
<div v-else>
|
||||
<template v-if="flow && !subtleLauncherRedirectUri">
|
||||
<label for="two-factor-code">
|
||||
<span class="label__title">{{ formatMessage(messages.twoFactorCodeLabel) }}</span>
|
||||
<span class="label__description">
|
||||
{{ formatMessage(messages.twoFactorCodeLabelDescription) }}
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
id="two-factor-code"
|
||||
v-model="twoFactorCodeModel"
|
||||
:maxlength="11"
|
||||
inputmode="numeric"
|
||||
:placeholder="formatMessage(messages.twoFactorCodeInputPlaceholder)"
|
||||
autocomplete="one-time-code"
|
||||
@keyup.enter="onTwoFactorSignIn()"
|
||||
/>
|
||||
|
||||
<button class="btn btn-primary continue-btn" @click="onTwoFactorSignIn()">
|
||||
{{ formatMessage(commonMessages.signInButton) }} <RightArrowIcon />
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="text-center text-2xl font-semibold text-contrast">
|
||||
{{ formatMessage(messages.signInWithLabel) }}
|
||||
</div>
|
||||
|
||||
<section class="flex flex-col gap-2.5">
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('google', redirectTarget)">
|
||||
<GoogleColorIcon />
|
||||
<span class="ml-1">{{
|
||||
formatMessage(messages.continueWithProvider, { provider: 'Google' })
|
||||
}}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('microsoft', redirectTarget)">
|
||||
<MicrosoftColorIcon />
|
||||
<span class="ml-1">{{
|
||||
formatMessage(messages.continueWithProvider, { provider: 'Microsoft' })
|
||||
}}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('discord', redirectTarget)">
|
||||
<DiscordColorIcon />
|
||||
<span class="ml-1">{{
|
||||
formatMessage(messages.continueWithProvider, { provider: 'Discord' })
|
||||
}}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('github', redirectTarget)">
|
||||
<GitHubColorIcon />
|
||||
<span class="ml-1">{{
|
||||
formatMessage(messages.continueWithProvider, { provider: 'GitHub' })
|
||||
}}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('gitlab', redirectTarget)">
|
||||
<GitLabColorIcon />
|
||||
<span class="ml-1">{{
|
||||
formatMessage(messages.continueWithProvider, { provider: 'GitLab' })
|
||||
}}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('steam', redirectTarget)">
|
||||
<SteamColorIcon />
|
||||
<span class="ml-1">{{
|
||||
formatMessage(messages.continueWithProvider, { provider: 'Steam' })
|
||||
}}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
</section>
|
||||
|
||||
<div class="h-px w-full bg-surface-5"></div>
|
||||
|
||||
<section class="auth-form">
|
||||
<label for="email" hidden>{{ formatMessage(commonMessages.emailUsernameLabel) }}</label>
|
||||
<StyledInput
|
||||
id="email"
|
||||
v-model="emailModel"
|
||||
:icon="MailIcon"
|
||||
type="text"
|
||||
inputmode="email"
|
||||
autocomplete="username"
|
||||
:placeholder="formatMessage(commonMessages.emailUsernameLabel)"
|
||||
wrapper-class="w-full"
|
||||
/>
|
||||
|
||||
<label for="password" hidden>{{ formatMessage(commonMessages.passwordLabel) }}</label>
|
||||
<StyledInput
|
||||
id="password"
|
||||
v-model="passwordModel"
|
||||
:icon="KeyIcon"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
:placeholder="formatMessage(commonMessages.passwordLabel)"
|
||||
wrapper-class="w-full"
|
||||
/>
|
||||
|
||||
<HCaptcha
|
||||
v-if="globals?.captcha_enabled && emailModel && passwordModel"
|
||||
:ref="onSetCaptchaRef"
|
||||
v-model="tokenModel"
|
||||
/>
|
||||
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
class="!w-full"
|
||||
:disabled="globals?.captcha_enabled ? !tokenModel : false"
|
||||
@click="onPasswordSignIn()"
|
||||
>
|
||||
{{ formatMessage(messages.continueWithEmail) }} <RightArrowIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
|
||||
<div class="auth-form__additional-options !text-base">
|
||||
<IntlFormatted :message-id="messages.additionalOptionsLabel">
|
||||
<template #forgot-password-link="{ children }">
|
||||
<NuxtLink
|
||||
class="text-link"
|
||||
:to="{
|
||||
path: '/auth/reset-password',
|
||||
query: routeQuery,
|
||||
}"
|
||||
>
|
||||
<component :is="() => children" />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<template #create-account-link="{ children }">
|
||||
<NuxtLink
|
||||
class="inline text-link"
|
||||
:to="{
|
||||
path: '/auth/sign-up',
|
||||
query: routeQuery,
|
||||
}"
|
||||
>
|
||||
<component :is="() => children" />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
DiscordColorIcon,
|
||||
GitHubColorIcon,
|
||||
GitLabColorIcon,
|
||||
GoogleColorIcon,
|
||||
KeyIcon,
|
||||
MailIcon,
|
||||
MicrosoftColorIcon,
|
||||
RightArrowIcon,
|
||||
SteamColorIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
IntlFormatted,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import HCaptcha from '@/components/ui/auth/HCaptcha.vue'
|
||||
import { getAuthUrl } from '@/composables/auth.ts'
|
||||
|
||||
const props = defineProps({
|
||||
subtleLauncherRedirectUri: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
flow: {
|
||||
default: '',
|
||||
},
|
||||
redirectTarget: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
routeQuery: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
globals: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
token: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
twoFactorCode: {
|
||||
default: null,
|
||||
},
|
||||
onPasswordSignIn: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
onTwoFactorSignIn: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
onSetCaptchaRef: {
|
||||
type: Function,
|
||||
default: undefined,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:email',
|
||||
'update:password',
|
||||
'update:token',
|
||||
'update:twoFactorCode',
|
||||
])
|
||||
|
||||
const emailModel = computed({
|
||||
get: () => props.email,
|
||||
set: (value) => emit('update:email', value),
|
||||
})
|
||||
|
||||
const passwordModel = computed({
|
||||
get: () => props.password,
|
||||
set: (value) => emit('update:password', value),
|
||||
})
|
||||
|
||||
const tokenModel = computed({
|
||||
get: () => props.token,
|
||||
set: (value) => emit('update:token', value),
|
||||
})
|
||||
|
||||
const twoFactorCodeModel = computed({
|
||||
get: () => props.twoFactorCode,
|
||||
set: (value) => emit('update:twoFactorCode', value),
|
||||
})
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
additionalOptionsLabel: {
|
||||
id: 'auth.sign-in.additional-options',
|
||||
defaultMessage:
|
||||
"<forgot-password-link>Forgot password</forgot-password-link> • Don't have an account? <create-account-link>Sign up</create-account-link>",
|
||||
},
|
||||
signInWithLabel: {
|
||||
id: 'auth.sign-in.sign-in-with',
|
||||
defaultMessage: 'Sign into Modrinth',
|
||||
},
|
||||
twoFactorCodeInputPlaceholder: {
|
||||
id: 'auth.sign-in.2fa.placeholder',
|
||||
defaultMessage: 'Enter code...',
|
||||
},
|
||||
twoFactorCodeLabel: {
|
||||
id: 'auth.sign-in.2fa.label',
|
||||
defaultMessage: 'Enter two-factor code',
|
||||
},
|
||||
twoFactorCodeLabelDescription: {
|
||||
id: 'auth.sign-in.2fa.description',
|
||||
defaultMessage: 'Please enter a two-factor code to proceed.',
|
||||
},
|
||||
continueWithProvider: {
|
||||
id: 'auth.continue-with-provider',
|
||||
defaultMessage: 'Continue with {provider}',
|
||||
},
|
||||
continueWithEmail: {
|
||||
id: 'auth.sign-in.continue-with-email',
|
||||
defaultMessage: 'Continue with Email',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="text-center text-2xl font-semibold text-contrast">
|
||||
{{ formatMessage(messages.signUpWithTitle) }}
|
||||
</div>
|
||||
<section class="flex flex-col gap-2.5">
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('google', redirectTarget)">
|
||||
<GoogleColorIcon />
|
||||
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'Google' }) }}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('microsoft', redirectTarget)">
|
||||
<MicrosoftColorIcon />
|
||||
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'Microsoft' }) }}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('discord', redirectTarget)">
|
||||
<DiscordColorIcon />
|
||||
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'Discord' }) }}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<template v-if="showOtherOptions">
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('github', redirectTarget)">
|
||||
<GitHubColorIcon />
|
||||
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'GitHub' }) }}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('gitlab', redirectTarget)">
|
||||
<GitLabColorIcon />
|
||||
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'GitLab' }) }}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<a class="!shadow-none" :href="getAuthUrl('steam', redirectTarget)">
|
||||
<SteamColorIcon />
|
||||
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'Steam' }) }}</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
<button
|
||||
class="mx-auto -mb-3 bg-transparent pt-1 text-center text-base font-semibold text-secondary transition-all hover:text-primary"
|
||||
@click="onToggleOtherOptions()"
|
||||
>
|
||||
{{
|
||||
showOtherOptions
|
||||
? formatMessage(messages.showFewerOptions)
|
||||
: formatMessage(messages.showOtherOptions)
|
||||
}}
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<div class="h-px w-full bg-surface-5"></div>
|
||||
|
||||
<section class="flex flex-col gap-2.5">
|
||||
<label for="email" hidden>{{ formatMessage(commonMessages.emailLabel) }}</label>
|
||||
<StyledInput
|
||||
id="email"
|
||||
v-model="emailModel"
|
||||
:icon="MailIcon"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
:placeholder="formatMessage(commonMessages.emailLabel)"
|
||||
wrapper-class="w-full"
|
||||
/>
|
||||
|
||||
<label for="password" hidden>{{ formatMessage(commonMessages.passwordLabel) }}</label>
|
||||
<StyledInput
|
||||
id="password"
|
||||
v-model="passwordModel"
|
||||
:icon="KeyIcon"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
:placeholder="formatMessage(commonMessages.passwordLabel)"
|
||||
wrapper-class="w-full"
|
||||
/>
|
||||
|
||||
<Checkbox
|
||||
v-model="subscribeModel"
|
||||
class="subscribe-btn"
|
||||
:label="formatMessage(messages.subscribeLabel)"
|
||||
:description="formatMessage(messages.subscribeLabel)"
|
||||
/>
|
||||
|
||||
<p v-if="!routeQuery.launcher">
|
||||
<IntlFormatted :message-id="messages.legalDisclaimer">
|
||||
<template #terms-link="{ children }">
|
||||
<NuxtLink to="/legal/terms" class="text-link">
|
||||
<component :is="() => children" />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<template #privacy-policy-link="{ children }">
|
||||
<NuxtLink to="/legal/privacy" class="text-link">
|
||||
<component :is="() => children" />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</p>
|
||||
|
||||
<HCaptcha
|
||||
v-if="globals?.captcha_enabled && emailModel && passwordModel"
|
||||
:ref="onSetCaptchaRef"
|
||||
v-model="tokenModel"
|
||||
/>
|
||||
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
class="!w-full"
|
||||
:disabled="globals?.captcha_enabled ? !tokenModel : false"
|
||||
@click="onCreateAccount()"
|
||||
>
|
||||
{{ formatMessage(messages.continueWithEmail) }} <RightArrowIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
|
||||
<div class="auth-form__additional-options">
|
||||
{{ formatMessage(messages.alreadyHaveAccountLabel) }}
|
||||
<NuxtLink
|
||||
class="text-link"
|
||||
:to="{
|
||||
path: '/auth/sign-in',
|
||||
query: routeQuery,
|
||||
}"
|
||||
>
|
||||
{{ formatMessage(commonMessages.signInButton) }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
DiscordColorIcon,
|
||||
GitHubColorIcon,
|
||||
GitLabColorIcon,
|
||||
GoogleColorIcon,
|
||||
KeyIcon,
|
||||
MailIcon,
|
||||
MicrosoftColorIcon,
|
||||
RightArrowIcon,
|
||||
SteamColorIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
IntlFormatted,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import HCaptcha from '@/components/ui/auth/HCaptcha.vue'
|
||||
import { getAuthUrl } from '@/composables/auth.ts'
|
||||
|
||||
const props = defineProps({
|
||||
redirectTarget: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
showOtherOptions: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
routeQuery: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
globals: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
token: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
subscribe: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
onToggleOtherOptions: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
onCreateAccount: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
onSetCaptchaRef: {
|
||||
type: Function,
|
||||
default: undefined,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:email', 'update:password', 'update:token', 'update:subscribe'])
|
||||
|
||||
const emailModel = computed({
|
||||
get: () => props.email,
|
||||
set: (value) => emit('update:email', value),
|
||||
})
|
||||
|
||||
const passwordModel = computed({
|
||||
get: () => props.password,
|
||||
set: (value) => emit('update:password', value),
|
||||
})
|
||||
|
||||
const tokenModel = computed({
|
||||
get: () => props.token,
|
||||
set: (value) => emit('update:token', value),
|
||||
})
|
||||
|
||||
const subscribeModel = computed({
|
||||
get: () => props.subscribe,
|
||||
set: (value) => emit('update:subscribe', value),
|
||||
})
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
signUpWithTitle: {
|
||||
id: 'auth.sign-up.title.sign-up-with',
|
||||
defaultMessage: 'Create an Account',
|
||||
},
|
||||
continueWithProvider: {
|
||||
id: 'auth.continue-with-provider',
|
||||
defaultMessage: 'Continue with {provider}',
|
||||
},
|
||||
subscribeLabel: {
|
||||
id: 'auth.sign-up.subscribe.label',
|
||||
defaultMessage: 'Subscribe to updates about Modrinth',
|
||||
},
|
||||
legalDisclaimer: {
|
||||
id: 'auth.sign-up.legal-dislaimer',
|
||||
defaultMessage:
|
||||
"By creating an account, you agree to Modrinth's <terms-link>Terms</terms-link> and <privacy-policy-link>Privacy Policy</privacy-policy-link>.",
|
||||
},
|
||||
alreadyHaveAccountLabel: {
|
||||
id: 'auth.sign-up.sign-in-option.title',
|
||||
defaultMessage: 'Already have an account?',
|
||||
},
|
||||
continueWithEmail: {
|
||||
id: 'auth.sign-up.continue-with-email',
|
||||
defaultMessage: 'Continue with Email',
|
||||
},
|
||||
showFewerOptions: {
|
||||
id: 'auth.sign-up.show-fewer-options',
|
||||
defaultMessage: 'Show fewer options',
|
||||
},
|
||||
showOtherOptions: {
|
||||
id: 'auth.sign-up.show-other-options',
|
||||
defaultMessage: 'Show other options',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user