developer #3
@ -11,11 +11,12 @@ const { prefersReducedMotion } = useMotion()
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const prefeitura = usePrefeituraStore()
|
const prefeitura = usePrefeituraStore()
|
||||||
const { isAuthenticated, nomeUsuario } = useAuth()
|
const { isAuthenticated, nomeUsuario, login } = useAuth()
|
||||||
const { requested: focusLoginRequested, consume: consumeFocusLogin } = useFocusLoginInput()
|
const { requested: focusLoginRequested, consume: consumeFocusLogin } = useFocusLoginInput()
|
||||||
|
|
||||||
const documento = ref('')
|
const documento = ref('')
|
||||||
const erro = ref('')
|
const erro = ref('')
|
||||||
|
const carregando = ref(false)
|
||||||
|
|
||||||
// Ref ao DocumentoInput — usado pelo botão "Entrar" do AppHeader pra focar o campo
|
// Ref ao DocumentoInput — usado pelo botão "Entrar" do AppHeader pra focar o campo
|
||||||
const documentoRef = ref(null)
|
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' },
|
{ icon: 'pi-user', titulo: 'Dados Cadastrais', descricao: 'Visualize e mantenha seus dados sempre atualizados.', to: '/portal/dados' },
|
||||||
]
|
]
|
||||||
|
|
||||||
function continuar() {
|
async function continuar() {
|
||||||
if (documento.value.replace(/\D/g, '').length < 11) {
|
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.'
|
erro.value = 'Informe um CPF (11 dígitos) ou CNPJ (14 dígitos) válido.'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
erro.value = ''
|
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>
|
</script>
|
||||||
|
|
||||||
@ -257,6 +266,7 @@ function continuar() {
|
|||||||
icon-pos="right"
|
icon-pos="right"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
size="large"
|
size="large"
|
||||||
|
:loading="carregando"
|
||||||
@click="continuar"
|
@click="continuar"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
<script setup>
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
import { useAuth } from '@/composables/useAuth'
|
||||||
import { primeiroAcessoService } from '@/services/primeiroAcessoService'
|
import { primeiroAcessoService } from '@/services/primeiroAcessoService'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { login } = useAuth()
|
||||||
|
|
||||||
const etapa = ref(0)
|
const etapa = ref(0)
|
||||||
const carregando = ref(false)
|
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 iconeCanal = { EMAIL: 'pi-envelope', SMS: 'pi-mobile', WHATSAPP: 'pi-whatsapp' }
|
||||||
const labelCanal = { EMAIL: 'E-mail', SMS: 'SMS', WHATSAPP: 'WhatsApp' }
|
const labelCanal = { EMAIL: 'E-mail', SMS: 'SMS', WHATSAPP: 'WhatsApp' }
|
||||||
</script>
|
</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.
|
Você já pode acessar o portal com seu CPF/CNPJ e a nova senha.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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>
|
</template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user