- main.css: @variant dark com seletor .app-dark (alinhado com PrimeVue darkModeSelector) - AccessibilityWidget: novo toggle "Modo escuro" no painel; preferências persistidas no localStorage (fonte, contraste, escuro) - PublicLayout/PortalLayout: dark:bg-slate-950/900, dark:border, dark:text em todos os elementos - AppHeader/AppFooter: dark variants em bg, border, textos e links - ServiceCard: usa cores primary em vez de blue hardcoded; dark variants completos - HomeView: dark nos avisos do carrossel (bg coloridos suavizados), card de login, seção de serviços autenticados e CTA - LoginView: dark no card, campo documento, label senha, links e botão voltar Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
173 lines
7.5 KiB
Vue
173 lines
7.5 KiB
Vue
<script setup>
|
|
import { ref, computed } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
const senha = ref('')
|
|
const carregando = ref(false)
|
|
const erro = ref('')
|
|
const senhaId = 'campo-senha'
|
|
const erroId = 'erro-senha'
|
|
|
|
const docBruto = computed(() => (route.query.doc ?? '').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({ name: 'home' })
|
|
}
|
|
|
|
async function entrar() {
|
|
if (!senha.value) {
|
|
erro.value = 'Informe a senha para continuar.'
|
|
return
|
|
}
|
|
erro.value = ''
|
|
carregando.value = true
|
|
// Integração com Keycloak PKCE — a ser implementado
|
|
setTimeout(() => {
|
|
carregando.value = false
|
|
erro.value = 'Integração com Keycloak ainda não configurada nesta fase.'
|
|
}, 800)
|
|
}
|
|
</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">
|
|
|
|
<!-- Cabeçalho — h1 para hierarquia de heading correta -->
|
|
<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>
|
|
<!-- Alvo de 44px via py-3 px-3 -->
|
|
<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>
|
|
|
|
<!-- Senha -->
|
|
<div>
|
|
<label :for="senhaId" class="block text-sm font-semibold text-slate-700 dark:text-slate-200 mb-1.5">
|
|
Senha
|
|
</label>
|
|
<Password
|
|
v-model="senha"
|
|
:input-id="senhaId"
|
|
:feedback="false"
|
|
toggle-mask
|
|
placeholder="Digite sua senha"
|
|
class="w-full"
|
|
input-class="w-full"
|
|
size="large"
|
|
:invalid="!!erro"
|
|
:input-props="{ 'aria-describedby': erro ? erroId : undefined, 'aria-invalid': !!erro || undefined }"
|
|
@keyup.enter="entrar"
|
|
/>
|
|
<!-- aria-live="assertive": anuncia o erro imediatamente para leitores de tela -->
|
|
<p
|
|
:id="erroId"
|
|
role="alert"
|
|
aria-live="assertive"
|
|
class="mt-2 text-sm text-red-700 font-medium flex items-center gap-1.5 min-h-[1.25rem]"
|
|
>
|
|
<template v-if="erro">
|
|
<i class="pi pi-exclamation-circle text-sm" aria-hidden="true" />
|
|
{{ erro }}
|
|
</template>
|
|
</p>
|
|
</div>
|
|
|
|
<Button
|
|
label="Entrar"
|
|
icon="pi pi-sign-in"
|
|
class="w-full"
|
|
size="large"
|
|
:loading="carregando"
|
|
@click="entrar"
|
|
/>
|
|
|
|
<div class="text-center space-y-1">
|
|
<RouterLink
|
|
:to="{ name: 'primeiro-acesso' }"
|
|
class="block text-sm text-primary font-medium py-2 hover:underline"
|
|
>
|
|
Esqueci minha senha
|
|
</RouterLink>
|
|
<RouterLink
|
|
:to="{ name: '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>
|
|
</RouterLink>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Voltar — alvo de 44px via py-3 -->
|
|
<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({ name: 'home' })"
|
|
>
|
|
<i class="pi pi-arrow-left text-xs" aria-hidden="true" />
|
|
Voltar à página inicial
|
|
</button>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</template>
|