mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-27 01:39:47 +00:00
[API Nodes] Signin/Signup dialog (#3466)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
118
src/components/dialog/content/SignInContent.vue
Normal file
118
src/components/dialog/content/SignInContent.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="w-96 p-2">
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col gap-4 mb-8">
|
||||
<h1 class="text-2xl font-medium leading-normal my-0">
|
||||
{{ isSignIn ? t('auth.login.title') : t('auth.signup.title') }}
|
||||
</h1>
|
||||
<p class="text-base my-0">
|
||||
<span class="text-muted">{{
|
||||
isSignIn
|
||||
? t('auth.login.newUser')
|
||||
: t('auth.signup.alreadyHaveAccount')
|
||||
}}</span>
|
||||
<span class="ml-1 cursor-pointer text-blue-500" @click="toggleState">{{
|
||||
isSignIn ? t('auth.login.signUp') : t('auth.signup.signIn')
|
||||
}}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Form -->
|
||||
<SignInForm v-if="isSignIn" @submit="signInWithEmail" />
|
||||
<SignUpForm v-else @submit="signInWithEmail" />
|
||||
|
||||
<!-- Divider -->
|
||||
<Divider align="center" layout="horizontal" class="my-8">
|
||||
<span class="text-muted">{{ t('auth.login.orContinueWith') }}</span>
|
||||
</Divider>
|
||||
|
||||
<!-- Social Login Buttons -->
|
||||
<div class="flex flex-col gap-6">
|
||||
<Button
|
||||
type="button"
|
||||
class="h-10"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="signInWithGoogle"
|
||||
>
|
||||
<i class="pi pi-google mr-2"></i>
|
||||
{{
|
||||
isSignIn
|
||||
? t('auth.login.loginWithGoogle')
|
||||
: t('auth.signup.signUpWithGoogle')
|
||||
}}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
class="h-10"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="signInWithGithub"
|
||||
>
|
||||
<i class="pi pi-github mr-2"></i>
|
||||
{{
|
||||
isSignIn
|
||||
? t('auth.login.loginWithGithub')
|
||||
: t('auth.signup.signUpWithGithub')
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
<!-- Terms -->
|
||||
<p class="text-xs text-muted mt-8">
|
||||
{{ t('auth.login.termsText') }}
|
||||
<span class="text-blue-500 cursor-pointer">{{
|
||||
t('auth.login.termsLink')
|
||||
}}</span>
|
||||
{{ t('auth.login.andText') }}
|
||||
<span class="text-blue-500 cursor-pointer">{{
|
||||
t('auth.login.privacyLink')
|
||||
}}</span
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Divider from 'primevue/divider'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { SignInData, SignUpData } from '@/schemas/signInSchema'
|
||||
|
||||
import SignInForm from './signin/SignInForm.vue'
|
||||
import SignUpForm from './signin/SignUpForm.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { onSuccess } = defineProps<{
|
||||
onSuccess: () => void
|
||||
}>()
|
||||
|
||||
const isSignIn = ref(true)
|
||||
const toggleState = () => {
|
||||
isSignIn.value = !isSignIn.value
|
||||
}
|
||||
|
||||
const signInWithGoogle = () => {
|
||||
// Implement Google login
|
||||
console.log(isSignIn.value)
|
||||
console.log('Google login clicked')
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
const signInWithGithub = () => {
|
||||
// Implement Github login
|
||||
console.log(isSignIn.value)
|
||||
console.log('Github login clicked')
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
const signInWithEmail = (values: SignInData | SignUpData) => {
|
||||
// Implement email login
|
||||
console.log(isSignIn.value)
|
||||
console.log(values)
|
||||
onSuccess()
|
||||
}
|
||||
</script>
|
||||
89
src/components/dialog/content/signin/SignInForm.vue
Normal file
89
src/components/dialog/content/signin/SignInForm.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<Form
|
||||
v-slot="$form"
|
||||
class="flex flex-col gap-6"
|
||||
:resolver="zodResolver(signInSchema)"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<!-- Email Field -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<label
|
||||
class="opacity-80 text-base font-medium mb-2"
|
||||
for="comfy-org-sign-in-email"
|
||||
>
|
||||
{{ t('auth.login.emailLabel') }}
|
||||
</label>
|
||||
<InputText
|
||||
pt:root:id="comfy-org-sign-in-email"
|
||||
pt:root:autocomplete="email"
|
||||
class="h-10"
|
||||
name="email"
|
||||
type="text"
|
||||
:placeholder="t('auth.login.emailPlaceholder')"
|
||||
:invalid="$form.email?.invalid"
|
||||
/>
|
||||
<small v-if="$form.email?.invalid" class="text-red-500">{{
|
||||
$form.email.error.message
|
||||
}}</small>
|
||||
</div>
|
||||
|
||||
<!-- Password Field -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<label
|
||||
class="opacity-80 text-base font-medium"
|
||||
for="comfy-org-sign-in-password"
|
||||
>
|
||||
{{ t('auth.login.passwordLabel') }}
|
||||
</label>
|
||||
<span class="text-muted text-base font-medium cursor-pointer">
|
||||
{{ t('auth.login.forgotPassword') }}
|
||||
</span>
|
||||
</div>
|
||||
<Password
|
||||
input-id="comfy-org-sign-in-password"
|
||||
pt:pc-input-text:root:autocomplete="current-password"
|
||||
name="password"
|
||||
:feedback="false"
|
||||
toggle-mask
|
||||
:placeholder="t('auth.login.passwordPlaceholder')"
|
||||
:class="{ 'p-invalid': $form.password?.invalid }"
|
||||
fluid
|
||||
class="h-10"
|
||||
/>
|
||||
<small v-if="$form.password?.invalid" class="text-red-500">{{
|
||||
$form.password.error.message
|
||||
}}</small>
|
||||
</div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<Button
|
||||
type="submit"
|
||||
:label="t('auth.login.loginButton')"
|
||||
class="h-10 font-medium mt-4"
|
||||
/>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Form, FormSubmitEvent } from '@primevue/forms'
|
||||
import { zodResolver } from '@primevue/forms/resolvers/zod'
|
||||
import Button from 'primevue/button'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Password from 'primevue/password'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { type SignInData, signInSchema } from '@/schemas/signInSchema'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [values: SignInData]
|
||||
}>()
|
||||
|
||||
const onSubmit = (event: FormSubmitEvent) => {
|
||||
if (event.valid) {
|
||||
emit('submit', event.values as SignInData)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
165
src/components/dialog/content/signin/SignUpForm.vue
Normal file
165
src/components/dialog/content/signin/SignUpForm.vue
Normal file
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<Form
|
||||
v-slot="$form"
|
||||
class="flex flex-col gap-6"
|
||||
:resolver="zodResolver(signUpSchema)"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<!-- Email Field -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<label
|
||||
class="opacity-80 text-base font-medium mb-2"
|
||||
for="comfy-org-sign-up-email"
|
||||
>
|
||||
{{ t('auth.signup.emailLabel') }}
|
||||
</label>
|
||||
<InputText
|
||||
pt:root:id="comfy-org-sign-up-email"
|
||||
pt:root:autocomplete="email"
|
||||
class="h-10"
|
||||
name="email"
|
||||
type="text"
|
||||
:placeholder="t('auth.signup.emailPlaceholder')"
|
||||
:invalid="$form.email?.invalid"
|
||||
/>
|
||||
<small v-if="$form.email?.invalid" class="text-red-500">{{
|
||||
$form.email.error.message
|
||||
}}</small>
|
||||
</div>
|
||||
|
||||
<!-- Password Field -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<label
|
||||
class="opacity-80 text-base font-medium"
|
||||
for="comfy-org-sign-up-password"
|
||||
>
|
||||
{{ t('auth.signup.passwordLabel') }}
|
||||
</label>
|
||||
</div>
|
||||
<Password
|
||||
v-model="password"
|
||||
input-id="comfy-org-sign-up-password"
|
||||
pt:pc-input-text:root:autocomplete="new-password"
|
||||
name="password"
|
||||
:feedback="false"
|
||||
toggle-mask
|
||||
:placeholder="t('auth.signup.passwordPlaceholder')"
|
||||
:class="{ 'p-invalid': $form.password?.invalid }"
|
||||
fluid
|
||||
class="h-10"
|
||||
/>
|
||||
<div class="flex flex-col gap-1">
|
||||
<small
|
||||
v-if="$form.password?.dirty || $form.password?.invalid"
|
||||
class="text-sm"
|
||||
>
|
||||
{{ t('validation.password.requirements') }}:
|
||||
<ul class="mt-1 space-y-1">
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.length
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.minLength') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.uppercase
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.uppercase') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.lowercase
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.lowercase') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.number
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.number') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.special
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.special') }}
|
||||
</li>
|
||||
</ul>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirm Password Field -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<label
|
||||
class="opacity-80 text-base font-medium mb-2"
|
||||
for="comfy-org-sign-up-confirm-password"
|
||||
>
|
||||
{{ t('auth.login.confirmPasswordLabel') }}
|
||||
</label>
|
||||
<Password
|
||||
name="confirmPassword"
|
||||
input-id="comfy-org-sign-up-confirm-password"
|
||||
pt:pc-input-text:root:autocomplete="new-password"
|
||||
:feedback="false"
|
||||
toggle-mask
|
||||
:placeholder="t('auth.login.confirmPasswordPlaceholder')"
|
||||
:class="{ 'p-invalid': $form.confirmPassword?.invalid }"
|
||||
fluid
|
||||
class="h-10"
|
||||
/>
|
||||
<small v-if="$form.confirmPassword?.error" class="text-red-500">{{
|
||||
$form.confirmPassword.error.message
|
||||
}}</small>
|
||||
</div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<Button
|
||||
type="submit"
|
||||
:label="t('auth.signup.signUpButton')"
|
||||
class="h-10 font-medium mt-4"
|
||||
/>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Form, FormSubmitEvent } from '@primevue/forms'
|
||||
import { zodResolver } from '@primevue/forms/resolvers/zod'
|
||||
import Button from 'primevue/button'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Password from 'primevue/password'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { type SignUpData, signUpSchema } from '@/schemas/signInSchema'
|
||||
|
||||
const { t } = useI18n()
|
||||
const password = ref('')
|
||||
|
||||
// TODO: Use dynamic form to better organize the password checks.
|
||||
// Ref: https://primevue.org/forms/#dynamic
|
||||
const passwordChecks = computed(() => ({
|
||||
length: password.value.length >= 8 && password.value.length <= 32,
|
||||
uppercase: /[A-Z]/.test(password.value),
|
||||
lowercase: /[a-z]/.test(password.value),
|
||||
number: /\d/.test(password.value),
|
||||
special: /[^A-Za-z0-9]/.test(password.value)
|
||||
}))
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [values: SignUpData]
|
||||
}>()
|
||||
|
||||
const onSubmit = (event: FormSubmitEvent) => {
|
||||
if (event.valid) {
|
||||
emit('submit', event.values as SignUpData)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user