melissa/perfil: ajustes UX nos cards novos

4 fixes pedidos no teste manual:

1. Card "Registro profissional" movido pra apos Identidade (em vez
   de antes do Layout). Faz sentido contextual — dados pessoais
   profissionais ficam juntos.

2. Inputs do Registro convertidos pra FloatLabel variant="on"
   (padrao Melissa do resto da tela). Tres campos: tipo, numero, uf
   + preview box.

3. Card "Preferencias" tema agora em 1 linha (grid 2-col fixo,
   classe .mpr-theme-row). Antes podia quebrar em 2 linhas via
   flex-wrap.

4. "Trocar senha" navega pra /melissa/seguranca (rota nativa
   Melissa, MelissaSeguranca.vue ja existente) em vez de
   /account/security (que sairia do shell Melissa). Nao vaza mais
   pro layout classico.

Styles novos extraidos do inline pro <style scoped>: mpr-preview-box,
mpr-theme-row, mpr-theme-card, mpr-info-row, mpr-action-card.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Leonardo
2026-05-21 11:14:29 -03:00
parent 20d2b3aee4
commit 7516468f78
+198 -87
View File
@@ -52,7 +52,8 @@ const UF_OPTIONS = [
].map(uf => ({ value: uf, label: uf })); ].map(uf => ({ value: uf, label: uf }));
function goSeguranca() { function goSeguranca() {
router.push('/account/security'); // Página nativa Melissa — não vaza pra layout clássico
router.push('/melissa/seguranca');
} }
// Troca de layout variant (classic/rail/melissa). Confirma + persiste + // Troca de layout variant (classic/rail/melissa). Confirma + persiste +
@@ -866,6 +867,78 @@ onBeforeUnmount(() => {
</div><!-- /.mpr-w__body --> </div><!-- /.mpr-w__body -->
</div> </div>
<!-- Registro Profissional (CFP #5) -->
<div id="mpr-sec-registro" class="mpr-w">
<div class="mpr-w__head">
<div class="mpr-w__icon"><i class="pi pi-id-card" /></div>
<div class="mpr-w__title">
<div class="mpr-w__title-text">Registro profissional</div>
<div class="mpr-w__sub">Conselho regional exigido para emissão de recibos, atestados e laudos</div>
</div>
</div>
<div class="mpr-w__body">
<div class="mpr-grid">
<div class="mpr-field mpr-field--half">
<FloatLabel variant="on">
<Select
id="mpr_reg_type"
v-model="form.professional_registration_type"
:options="REGISTRATION_TYPE_OPTIONS"
optionLabel="label"
optionValue="value"
class="w-full"
@change="markDirty"
/>
<label for="mpr_reg_type">Tipo de registro</label>
</FloatLabel>
<small class="mpr-hint">Conselho profissional ao qual você é vinculado.</small>
</div>
<div class="mpr-field mpr-field--half">
<FloatLabel variant="on">
<InputText
id="mpr_reg_number"
v-model="form.professional_registration_number"
class="w-full"
:disabled="!form.professional_registration_type"
@input="markDirty"
/>
<label for="mpr_reg_number">Número do registro</label>
</FloatLabel>
<small class="mpr-hint">Ex: 06/12345</small>
</div>
<div class="mpr-field mpr-field--half">
<FloatLabel variant="on">
<Select
id="mpr_reg_uf"
v-model="form.professional_registration_uf"
:options="UF_OPTIONS"
optionLabel="label"
optionValue="value"
:disabled="!form.professional_registration_type"
:filter="true"
class="w-full"
@change="markDirty"
/>
<label for="mpr_reg_uf">UF</label>
</FloatLabel>
<small class="mpr-hint">Estado do conselho.</small>
</div>
<div v-if="form.professional_registration_type && form.professional_registration_number" class="mpr-field mpr-field--full">
<div class="mpr-preview-box">
<span class="mpr-preview-label">Aparecerá nos documentos como:</span>
<strong class="mpr-preview-value">
{{ form.professional_registration_type }}
{{ form.professional_registration_number }}{{ form.professional_registration_uf ? '/' + form.professional_registration_uf : '' }}
</strong>
</div>
</div>
</div>
</div><!-- /.mpr-w__body -->
</div>
<!-- Contato --> <!-- Contato -->
<div id="mpr-sec-contato" class="mpr-w"> <div id="mpr-sec-contato" class="mpr-w">
<div class="mpr-w__head"> <div class="mpr-w__head">
@@ -1017,63 +1090,6 @@ onBeforeUnmount(() => {
</div><!-- /.mpr-w__body --> </div><!-- /.mpr-w__body -->
</div> </div>
<!-- Registro Profissional (CFP #5) -->
<div id="mpr-sec-registro" class="mpr-w">
<div class="mpr-w__head">
<div class="mpr-w__icon" style="background: rgba(14,165,233,0.1); color: #0ea5e9"><i class="pi pi-id-card" /></div>
<div class="mpr-w__title">
<div class="mpr-w__title-text">Registro profissional</div>
<div class="mpr-w__sub">Conselho regional exigido para emissão de recibos, atestados e laudos</div>
</div>
</div>
<div class="mpr-w__body">
<div class="mpr-grid">
<div class="mpr-field mpr-col-6">
<label class="mpr-label">Tipo de registro</label>
<Select
v-model="form.professional_registration_type"
:options="REGISTRATION_TYPE_OPTIONS"
optionLabel="label"
optionValue="value"
class="w-full"
@update:modelValue="markDirty"
/>
<small class="mpr-hint">Conselho profissional ao qual você é vinculado.</small>
</div>
<div class="mpr-field mpr-col-3">
<label class="mpr-label">Número</label>
<InputText
v-model="form.professional_registration_number"
class="w-full"
:disabled="!form.professional_registration_type"
@input="markDirty"
/>
<small class="mpr-hint">Ex: 06/12345</small>
</div>
<div class="mpr-field mpr-col-3">
<label class="mpr-label">UF</label>
<Select
v-model="form.professional_registration_uf"
:options="UF_OPTIONS"
optionLabel="label"
optionValue="value"
:disabled="!form.professional_registration_type"
:filter="true"
class="w-full"
@update:modelValue="markDirty"
/>
<small class="mpr-hint">Estado do conselho.</small>
</div>
<div v-if="form.professional_registration_type && form.professional_registration_number" class="mpr-col-12">
<div style="background: rgba(14,165,233,0.08); border: 1px solid rgba(14,165,233,0.2); border-radius: 6px; padding: 10px 14px; font-size: 0.9rem;">
<span style="opacity: 0.7; margin-right: 8px;">Aparecerá nos documentos como:</span>
<strong>{{ form.professional_registration_type }} {{ form.professional_registration_number }}{{ form.professional_registration_uf ? '/' + form.professional_registration_uf : '' }}</strong>
</div>
</div>
</div>
</div>
</div>
<!-- Layout (variante de navegação) --> <!-- Layout (variante de navegação) -->
<div id="mpr-sec-layout" class="mpr-w"> <div id="mpr-sec-layout" class="mpr-w">
<div class="mpr-w__head"> <div class="mpr-w__head">
@@ -1163,51 +1179,49 @@ onBeforeUnmount(() => {
<!-- Preferências (tema + aparência) --> <!-- Preferências (tema + aparência) -->
<div id="mpr-sec-preferencias" class="mpr-w"> <div id="mpr-sec-preferencias" class="mpr-w">
<div class="mpr-w__head"> <div class="mpr-w__head">
<div class="mpr-w__icon" style="background: rgba(168,85,247,0.1); color: #a855f7"><i class="pi pi-palette" /></div> <div class="mpr-w__icon"><i class="pi pi-palette" /></div>
<div class="mpr-w__title"> <div class="mpr-w__title">
<div class="mpr-w__title-text">Preferências</div> <div class="mpr-w__title-text">Preferências</div>
<div class="mpr-w__sub">Aparência do sistema tema e densidade</div> <div class="mpr-w__sub">Aparência do sistema</div>
</div> </div>
</div> </div>
<div class="mpr-w__body"> <div class="mpr-w__body">
<div class="mpr-grid"> <div class="mpr-grid">
<div class="mpr-field mpr-col-12"> <div class="mpr-field mpr-field--full">
<label class="mpr-label">Tema</label> <label class="mpr-label">Tema</label>
<div style="display: flex; gap: 10px; flex-wrap: wrap;"> <div class="mpr-theme-row">
<button <button
type="button" type="button"
class="mpr-lv-card" class="mpr-lv-card mpr-theme-card"
:class="{ 'mpr-lv-card--current': !isDarkTheme }" :class="{ 'mpr-lv-card--current': !isDarkTheme }"
style="padding: 14px 18px; flex: 1; min-width: 180px; display: flex; align-items: center; gap: 12px;"
@click="isDarkTheme && toggleDarkMode()" @click="isDarkTheme && toggleDarkMode()"
> >
<i class="pi pi-sun" style="font-size: 1.3rem; color: #f59e0b;" /> <i class="pi pi-sun mpr-theme-icon" style="color: #f59e0b;" />
<div style="text-align: left;"> <div class="mpr-theme-text">
<div class="mpr-lv-name">Claro</div> <div class="mpr-lv-name">Claro</div>
<div class="mpr-lv-sub">Fundo branco, contraste alto</div> <div class="mpr-lv-sub">Fundo branco</div>
</div> </div>
<div class="mpr-lv-radio" style="margin-left: auto;"> <div class="mpr-lv-radio">
<div v-if="!isDarkTheme" class="mpr-lv-dot" /> <div v-if="!isDarkTheme" class="mpr-lv-dot" />
</div> </div>
</button> </button>
<button <button
type="button" type="button"
class="mpr-lv-card" class="mpr-lv-card mpr-theme-card"
:class="{ 'mpr-lv-card--current': isDarkTheme }" :class="{ 'mpr-lv-card--current': isDarkTheme }"
style="padding: 14px 18px; flex: 1; min-width: 180px; display: flex; align-items: center; gap: 12px;"
@click="!isDarkTheme && toggleDarkMode()" @click="!isDarkTheme && toggleDarkMode()"
> >
<i class="pi pi-moon" style="font-size: 1.3rem; color: #6366f1;" /> <i class="pi pi-moon mpr-theme-icon" style="color: #6366f1;" />
<div style="text-align: left;"> <div class="mpr-theme-text">
<div class="mpr-lv-name">Escuro</div> <div class="mpr-lv-name">Escuro</div>
<div class="mpr-lv-sub">Fundo escuro, menos fadiga visual</div> <div class="mpr-lv-sub">Menos fadiga visual</div>
</div> </div>
<div class="mpr-lv-radio" style="margin-left: auto;"> <div class="mpr-lv-radio">
<div v-if="isDarkTheme" class="mpr-lv-dot" /> <div v-if="isDarkTheme" class="mpr-lv-dot" />
</div> </div>
</button> </button>
</div> </div>
<small class="mpr-hint">A preferência é salva no seu perfil e segue você em qualquer navegador.</small> <small class="mpr-hint">A preferência é salva no seu perfil.</small>
</div> </div>
</div> </div>
</div><!-- /.mpr-w__body --> </div><!-- /.mpr-w__body -->
@@ -1216,7 +1230,7 @@ onBeforeUnmount(() => {
<!-- Segurança --> <!-- Segurança -->
<div id="mpr-sec-seguranca" class="mpr-w"> <div id="mpr-sec-seguranca" class="mpr-w">
<div class="mpr-w__head"> <div class="mpr-w__head">
<div class="mpr-w__icon" style="background: rgba(239,68,68,0.1); color: #ef4444"><i class="pi pi-shield" /></div> <div class="mpr-w__icon"><i class="pi pi-shield" /></div>
<div class="mpr-w__title"> <div class="mpr-w__title">
<div class="mpr-w__title-text">Segurança</div> <div class="mpr-w__title-text">Segurança</div>
<div class="mpr-w__sub">Senha e proteção da conta</div> <div class="mpr-w__sub">Senha e proteção da conta</div>
@@ -1224,23 +1238,23 @@ onBeforeUnmount(() => {
</div> </div>
<div class="mpr-w__body"> <div class="mpr-w__body">
<div class="mpr-grid"> <div class="mpr-grid">
<div class="mpr-field mpr-col-12"> <div class="mpr-field mpr-field--full">
<div style="display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 14px; border: 1px solid var(--m-border, rgba(0,0,0,0.1)); border-radius: 8px;"> <div class="mpr-info-row">
<div style="flex: 1; min-width: 200px;"> <div class="mpr-info-text">
<div style="font-weight: 600; margin-bottom: 4px;">E-mail de acesso</div> <div class="mpr-info-title">E-mail de acesso</div>
<div style="opacity: 0.7; font-size: 0.9rem;">{{ userEmail }}</div> <div class="mpr-info-value">{{ userEmail }}</div>
</div> </div>
<small class="mpr-hint" style="margin: 0;">Para trocar o e-mail, contate o suporte.</small> <small class="mpr-hint mpr-info-hint">Para trocar o e-mail, contate o suporte.</small>
</div> </div>
</div> </div>
<div class="mpr-field mpr-col-12"> <div class="mpr-field mpr-field--full">
<button type="button" class="mpr-lv-card" style="display: flex; align-items: center; gap: 14px; padding: 14px 18px; width: 100%; text-align: left;" @click="goSeguranca"> <button type="button" class="mpr-lv-card mpr-action-card" @click="goSeguranca">
<i class="pi pi-key" style="font-size: 1.4rem; color: #ef4444;" /> <i class="pi pi-key mpr-action-icon" />
<div style="flex: 1;"> <div class="mpr-action-text">
<div class="mpr-lv-name">Trocar senha</div> <div class="mpr-lv-name">Trocar senha</div>
<div class="mpr-lv-sub">Atualize sua senha de acesso ao sistema</div> <div class="mpr-lv-sub">Atualize sua senha de acesso ao sistema</div>
</div> </div>
<i class="pi pi-arrow-right" style="opacity: 0.5;" /> <i class="pi pi-arrow-right mpr-action-arrow" />
</button> </button>
</div> </div>
</div> </div>
@@ -2120,4 +2134,101 @@ onBeforeUnmount(() => {
line-height: 1.3; line-height: 1.3;
margin-top: 2px; margin-top: 2px;
} }
/* ═══════════ Registro Profissional — preview box ═══════════ */
.mpr-preview-box {
background: color-mix(in srgb, var(--p-primary-color) 7%, transparent);
border: 1px solid color-mix(in srgb, var(--p-primary-color) 22%, var(--surface-border));
border-radius: 8px;
padding: 10px 14px;
font-size: 0.88rem;
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.mpr-preview-label {
color: var(--text-color-secondary);
}
.mpr-preview-value {
color: var(--text-color);
font-weight: 600;
letter-spacing: 0.01em;
}
/* ═══════════ Preferências — tema em 1 linha ═══════════ */
.mpr-theme-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.mpr-theme-card {
padding: 14px 16px;
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
text-align: left;
}
.mpr-theme-icon {
font-size: 1.3rem;
flex-shrink: 0;
}
.mpr-theme-text {
flex: 1;
min-width: 0;
}
/* ═══════════ Segurança — info row + action card ═══════════ */
.mpr-info-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
padding: 14px;
border: 1px solid var(--surface-border);
border-radius: 8px;
background: var(--surface-ground, transparent);
}
.mpr-info-text {
flex: 1;
min-width: 0;
}
.mpr-info-title {
font-weight: 600;
margin-bottom: 4px;
color: var(--text-color);
}
.mpr-info-value {
color: var(--text-color-secondary);
font-size: 0.9rem;
}
.mpr-info-hint {
margin: 0;
flex-shrink: 0;
}
.mpr-action-card {
display: flex;
align-items: center;
gap: 14px;
padding: 14px 18px;
width: 100%;
text-align: left;
cursor: pointer;
}
.mpr-action-icon {
font-size: 1.4rem;
color: var(--text-color);
flex-shrink: 0;
}
.mpr-action-text {
flex: 1;
min-width: 0;
}
.mpr-action-arrow {
opacity: 0.5;
flex-shrink: 0;
}
</style> </style>