diff --git a/deploy/keycloak/themes/synapse/login/resources/css/synapse.css b/deploy/keycloak/themes/synapse/login/resources/css/synapse.css index 950d7fb..79389da 100644 --- a/deploy/keycloak/themes/synapse/login/resources/css/synapse.css +++ b/deploy/keycloak/themes/synapse/login/resources/css/synapse.css @@ -66,7 +66,7 @@ body.synapse-login-page { .synapse-trust li span { color: #d15c17; font-size: 1rem; } /* Ajustes no Card de Login baseados no Figma */ -.synapse-card { width: 480px; max-width: 480px; height: 667px; align-self: center; justify-self: end; padding: 3rem 2.25rem 2rem; border: 1px solid rgb(220 193 183 / 50%); border-radius: 24px; color: #2e1a10; background: #fff; box-shadow: 0 30px 70px -15px rgb(30 20 17 / 12%), 0 0 0 1px rgb(220 193 183 / 35%); } +.synapse-card { width: 480px; max-width: 480px; height: 600px; align-self: center; justify-self: end; padding: 3rem 2.25rem 2rem; border: 1px solid rgb(220 193 183 / 50%); border-radius: 24px; color: #2e1a10; background: #fff; box-shadow: 0 30px 70px -15px rgb(30 20 17 / 12%), 0 0 0 1px rgb(220 193 183 / 35%); } .synapse-card-icon { display: grid; width: 2.7rem; height: 2.7rem; place-items: center; border: 1px solid #fce8db; border-radius: .5rem; color: #c65a0d; background: #fff4ed; } .synapse-card-icon svg { width: 1.2rem; height: 1.2rem; } .synapse-card h2 { margin: .95rem 0 .35rem; font-family: Georgia, Cambria, serif; font-size: clamp(1.65rem, 2vw, 2rem); font-weight: 400; letter-spacing: -.02em; } diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico deleted file mode 100644 index df36fcf..0000000 Binary files a/frontend/public/favicon.ico and /dev/null differ diff --git a/frontend/public/synapse.ico b/frontend/public/synapse.ico new file mode 100644 index 0000000..2cc0862 Binary files /dev/null and b/frontend/public/synapse.ico differ diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index 8b3b60b..e56a7a7 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -7,4 +7,18 @@ body, min-width: 100%; min-height: 100%; margin: 0; + overflow-x: clip; +} + +/* + * O monitor de referência tem 1920px CSS (120rem a 16px). Um notebook com + * escala de 125% normalmente disponibiliza entre 1200px e 1600px CSS; ao + * tornar o rem proporcional nessa faixa, a composição ganha a mesma presença + * visual do desktop de referência e continua fluida, sem canvas fixo ou + * rolagem horizontal. + */ +@media (min-resolution: 1.25dppx) and (min-width: 75rem) and (max-width: 100rem) { + html { + font-size: calc(100vw / 110); + } } diff --git a/frontend/src/components/TheProcessHeader.spec.ts b/frontend/src/components/TheProcessHeader.spec.ts new file mode 100644 index 0000000..0bc95d8 --- /dev/null +++ b/frontend/src/components/TheProcessHeader.spec.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from 'vitest' +import { mount } from '@vue/test-utils' +import { createPinia } from 'pinia' +import TheProcessHeader from './TheProcessHeader.vue' + +const opcoesDeMontagem = { + global: { + plugins: [createPinia()], + }, +} + +describe('TheProcessHeader', () => { + it('marca a entrada de regra como etapa atual', () => { + const conteiner = mount(TheProcessHeader, { ...opcoesDeMontagem, props: { etapaDaRota: 'voz-texto' } }) + + expect(conteiner.get('li[aria-current="step"]').text()).toContain('Voz/Texto') + }) + + it('associa os estados intermediários à simulação enquanto a conversa não possui tela própria', () => { + const conteiner = mount(TheProcessHeader, { + ...opcoesDeMontagem, + props: { etapaDaRota: 'simulacao', statusDoJob: 'aguardando_confirmacao_parametros' }, + }) + + expect(conteiner.get('li[aria-current="step"]').text()).toContain('Simulação') + expect(conteiner.text()).not.toContain('Conversa') + }) + + it('indica o salvamento em toda a tela de finalização, mesmo durante uma transição de status', () => { + const conteiner = mount(TheProcessHeader, { + ...opcoesDeMontagem, + props: { etapaDaRota: 'salvar', statusDoJob: 'simulacao_inviavel' }, + }) + + expect(conteiner.get('li[aria-current="step"]').text()).toContain('Salvar') + }) + + it('não exibe o progresso de uma nova regra no histórico', () => { + const conteiner = mount(TheProcessHeader, { ...opcoesDeMontagem, props: { etapaDaRota: null } }) + + expect(conteiner.find('[aria-label="Etapas do processo"]').exists()).toBe(false) + expect(conteiner.find('[aria-label^="Etapa atual:"]').exists()).toBe(false) + }) + + it('mantém um indicador textual acessível fora do desktop', () => { + const conteiner = mount(TheProcessHeader, { ...opcoesDeMontagem, props: { etapaDaRota: 'simulacao' } }) + const indicador = conteiner.get('[aria-label="Etapa atual: Simulação"]') + + expect(indicador.text()).toContain('Etapa: Simulação') + expect(indicador.classes()).toContain('xl:hidden') + }) + + it('usa um ícone reconhecível para abrir o menu de perfil', () => { + const conteiner = mount(TheProcessHeader, { ...opcoesDeMontagem, props: { etapaDaRota: 'voz-texto' } }) + const menuDePerfil = conteiner.get('summary[aria-label="Abrir menu de perfil"]') + + expect(menuDePerfil.find('svg').attributes('viewBox')).toBe('0 0 24 24') + expect(menuDePerfil.text()).not.toContain('U') + }) +}) diff --git a/frontend/src/components/TheProcessHeader.vue b/frontend/src/components/TheProcessHeader.vue index 131ec08..3806c75 100644 --- a/frontend/src/components/TheProcessHeader.vue +++ b/frontend/src/components/TheProcessHeader.vue @@ -1,10 +1,50 @@