feat/nuxt-bff #1
@ -11,11 +11,12 @@ const { prefersReducedMotion } = useMotion()
|
||||
|
||||
const router = useRouter()
|
||||
const prefeitura = usePrefeituraStore()
|
||||
const { isAuthenticated, nomeUsuario } = useAuth()
|
||||
const { isAuthenticated, nomeUsuario, login } = useAuth()
|
||||
const { requested: focusLoginRequested, consume: consumeFocusLogin } = useFocusLoginInput()
|
||||
|
||||
const documento = ref('')
|
||||
const erro = ref('')
|
||||
const carregando = ref(false)
|
||||
|
||||
// Ref ao DocumentoInput — usado pelo botão "Entrar" do AppHeader pra focar o campo
|
||||
const documentoRef = ref(null)
|
||||
@ -103,13 +104,21 @@ const servicosAutenticados = [
|
||||
{ icon: 'pi-user', titulo: 'Dados Cadastrais', descricao: 'Visualize e mantenha seus dados sempre atualizados.', to: '/portal/dados' },
|
||||
]
|
||||
|
||||
function continuar() {
|
||||
if (documento.value.replace(/\D/g, '').length < 11) {
|
||||
async function continuar() {
|
||||
const doc = documento.value.replace(/\D/g, '')
|
||||
if (doc.length < 11) {
|
||||
erro.value = 'Informe um CPF (11 dígitos) ou CNPJ (14 dígitos) válido.'
|
||||
return
|
||||
}
|
||||
erro.value = ''
|
||||
router.push({ path: '/login', query: { doc: documento.value } })
|
||||
carregando.value = true
|
||||
try {
|
||||
await login(doc, '/portal/painel')
|
||||
// login() faz window.location → não retorna aqui em condições normais
|
||||
} catch (e) {
|
||||
carregando.value = false
|
||||
erro.value = e?.data?.statusMessage ?? 'Não foi possível iniciar o login.'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -257,6 +266,7 @@ function continuar() {
|
||||
icon-pos="right"
|
||||
class="w-full"
|
||||
size="large"
|
||||
:loading="carregando"
|
||||
@click="continuar"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,152 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { login } = useAuth()
|
||||
|
||||
const carregando = ref(false)
|
||||
const erro = ref('')
|
||||
|
||||
const docBruto = computed(() => (route.query.doc ?? '').toString().replace(/\D/g, ''))
|
||||
|
||||
const docFormatado = computed(() => {
|
||||
const d = docBruto.value
|
||||
if (d.length <= 11) {
|
||||
return d
|
||||
.replace(/(\d{3})(\d)/, '$1.$2')
|
||||
.replace(/(\d{3})(\d)/, '$1.$2')
|
||||
.replace(/(\d{3})(\d{1,2})$/, '$1-$2')
|
||||
}
|
||||
return d
|
||||
.replace(/(\d{2})(\d)/, '$1.$2')
|
||||
.replace(/(\d{3})(\d)/, '$1.$2')
|
||||
.replace(/(\d{3})(\d)/, '$1/$2')
|
||||
.replace(/(\d{4})(\d{1,2})$/, '$1-$2')
|
||||
})
|
||||
|
||||
const tipoDoc = computed(() => docBruto.value.length > 11 ? 'CNPJ' : 'CPF')
|
||||
|
||||
function trocarDocumento() {
|
||||
router.push('/')
|
||||
}
|
||||
|
||||
async function entrar() {
|
||||
if (!docBruto.value) {
|
||||
erro.value = 'Documento não informado. Volte e informe seu CPF/CNPJ.'
|
||||
return
|
||||
}
|
||||
erro.value = ''
|
||||
carregando.value = true
|
||||
try {
|
||||
await login(docBruto.value, '/portal/painel')
|
||||
// login() faz window.location para o Keycloak — não retorna aqui
|
||||
} catch (e) {
|
||||
carregando.value = false
|
||||
erro.value = e?.data?.statusMessage ?? 'Não foi possível iniciar o login. Tente novamente.'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-[calc(100vh-8rem)] flex items-center justify-center px-4 py-12">
|
||||
<div class="w-full max-w-md">
|
||||
|
||||
<div class="bg-white dark:bg-slate-800 rounded-2xl shadow-lg border border-slate-200 dark:border-slate-700 overflow-hidden">
|
||||
|
||||
<div class="bg-gradient-to-r from-primary-700 to-primary-800 px-8 py-6 bg-primary">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 bg-white/20 rounded-xl flex items-center justify-center" aria-hidden="true">
|
||||
<i class="pi pi-lock text-white text-lg" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-white font-bold text-base leading-tight">Acesso seguro</h1>
|
||||
<p class="text-white/80 text-xs mt-0.5">Portal do Contribuinte</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-8 py-8 space-y-6">
|
||||
|
||||
<!-- Documento identificado -->
|
||||
<div>
|
||||
<p class="text-xs font-semibold text-slate-600 dark:text-slate-400 uppercase tracking-wide mb-2">
|
||||
Entrando como
|
||||
</p>
|
||||
<div class="flex items-center justify-between bg-slate-50 dark:bg-slate-700 border border-slate-200 dark:border-slate-600 rounded-xl px-4 py-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-8 h-8 bg-primary/10 rounded-lg flex items-center justify-center" aria-hidden="true">
|
||||
<i class="pi pi-id-card text-primary text-sm" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-mono font-semibold text-slate-800 dark:text-slate-100 text-sm">{{ docFormatado }}</p>
|
||||
<p class="text-xs text-slate-600 dark:text-slate-300">{{ tipoDoc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="inline-flex items-center gap-1.5 text-sm text-primary hover:text-primary font-semibold px-3 py-3 rounded-lg hover:bg-primary/8 transition-colors"
|
||||
aria-label="Trocar documento de identificação"
|
||||
@click="trocarDocumento"
|
||||
>
|
||||
<i class="pi pi-pencil text-xs" aria-hidden="true" />
|
||||
Trocar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-slate-600 dark:text-slate-300 leading-relaxed">
|
||||
Você será redirecionado para o serviço de autenticação seguro
|
||||
onde deverá digitar sua senha.
|
||||
</p>
|
||||
|
||||
<p
|
||||
v-if="erro"
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
class="text-sm text-red-700 font-medium flex items-center gap-1.5"
|
||||
>
|
||||
<i class="pi pi-exclamation-circle text-sm" aria-hidden="true" />
|
||||
{{ erro }}
|
||||
</p>
|
||||
|
||||
<Button
|
||||
label="Continuar para autenticação"
|
||||
icon="pi pi-sign-in"
|
||||
class="w-full"
|
||||
size="large"
|
||||
:loading="carregando"
|
||||
@click="entrar"
|
||||
/>
|
||||
|
||||
<div class="text-center space-y-1">
|
||||
<NuxtLink
|
||||
to="/primeiro-acesso"
|
||||
class="block text-sm text-primary font-medium py-2 hover:underline"
|
||||
>
|
||||
Esqueci minha senha
|
||||
</NuxtLink>
|
||||
<NuxtLink
|
||||
to="/credenciamento"
|
||||
class="block text-sm text-slate-600 dark:text-slate-400 py-2 hover:text-slate-800 dark:hover:text-slate-200 transition-colors"
|
||||
>
|
||||
Ainda não tem acesso? <span class="text-primary font-semibold">Credenciar-se</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<button
|
||||
class="inline-flex items-center gap-2 text-sm text-slate-600 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-200 transition-colors py-3 px-4 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800"
|
||||
@click="router.push('/')"
|
||||
>
|
||||
<i class="pi pi-arrow-left text-xs" aria-hidden="true" />
|
||||
Voltar à página inicial
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,8 +1,10 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { primeiroAcessoService } from '@/services/primeiroAcessoService'
|
||||
|
||||
const router = useRouter()
|
||||
const { login } = useAuth()
|
||||
|
||||
const etapa = ref(0)
|
||||
const carregando = ref(false)
|
||||
@ -86,6 +88,17 @@ async function definirSenha() {
|
||||
}
|
||||
}
|
||||
|
||||
async function entrarKeycloak() {
|
||||
carregando.value = true
|
||||
erro.value = ''
|
||||
try {
|
||||
await login(docDigitos.value, '/portal/painel')
|
||||
} catch (e) {
|
||||
carregando.value = false
|
||||
erro.value = e?.data?.statusMessage ?? 'Não foi possível iniciar o login.'
|
||||
}
|
||||
}
|
||||
|
||||
const iconeCanal = { EMAIL: 'pi-envelope', SMS: 'pi-mobile', WHATSAPP: 'pi-whatsapp' }
|
||||
const labelCanal = { EMAIL: 'E-mail', SMS: 'SMS', WHATSAPP: 'WhatsApp' }
|
||||
</script>
|
||||
@ -224,7 +237,7 @@ const labelCanal = { EMAIL: 'E-mail', SMS: 'SMS', WHATSAPP: 'WhatsApp' }
|
||||
Você já pode acessar o portal com seu CPF/CNPJ e a nova senha.
|
||||
</p>
|
||||
</div>
|
||||
<Button label="Ir para o login" icon="pi pi-sign-in" class="w-full" size="large" @click="router.push({ path: '/login', query: { doc: docDigitos } })" />
|
||||
<Button label="Ir para o login" icon="pi pi-sign-in" class="w-full" size="large" :loading="carregando" @click="entrarKeycloak" />
|
||||
</template>
|
||||
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user