profile: UI dos campos de registro profissional (CFP #5)
Gap detectado em teste manual: migration 20260521000003 adicionou as 3 colunas (professional_registration_type/_number/_uf) e o DocumentGenerate.service.loadTherapistData ja le delas, mas a UI de edicao nao foi criada. ProfilePage.vue ganha novo card "Registro Profissional" (id= registro-profissional, cor #0ea5e9 ciano, antes do card de Redes Sociais): - Select tipo (CRP/CRM/CRFa/CREFITO/CRESS/CRN/RMS/outro — mesmas opcoes do CHECK constraint) - InputText numero - Select UF (27 estados, filterable) - Preview: "Aparecera nos documentos como: CRP 06/12345/SP" - Numero e UF disabled enquanto tipo nao escolhido Wire-up: SELECT/UPDATE do profile agora incluem as 3 colunas. form.* tem defaults vazios. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -103,6 +103,11 @@ const form = reactive({
|
||||
bio: '',
|
||||
phone: '',
|
||||
|
||||
// Registro profissional (CFP #5 — exigido pra emissão de recibos/laudos)
|
||||
professional_registration_type: '',
|
||||
professional_registration_number: '',
|
||||
professional_registration_uf: '',
|
||||
|
||||
site_url: '',
|
||||
social_instagram: '',
|
||||
social_youtube: '',
|
||||
@@ -117,6 +122,24 @@ const form = reactive({
|
||||
notify_news: false
|
||||
});
|
||||
|
||||
// Opções do CHECK constraint da migration 20260521000003
|
||||
const REGISTRATION_TYPE_OPTIONS = [
|
||||
{ value: '', label: '— Não informado —' },
|
||||
{ value: 'CRP', label: 'CRP — Psicólogo(a)' },
|
||||
{ value: 'CRM', label: 'CRM — Médico(a)' },
|
||||
{ value: 'CRFa', label: 'CRFa — Fonoaudiólogo(a)' },
|
||||
{ value: 'CREFITO', label: 'CREFITO — Fisioterapeuta / T.O.' },
|
||||
{ value: 'CRESS', label: 'CRESS — Assistente Social' },
|
||||
{ value: 'CRN', label: 'CRN — Nutricionista' },
|
||||
{ value: 'RMS', label: 'RMS — Residência Multiprofissional' },
|
||||
{ value: 'outro', label: 'Outro' }
|
||||
];
|
||||
|
||||
const UF_OPTIONS = [
|
||||
'AC','AL','AP','AM','BA','CE','DF','ES','GO','MA','MT','MS','MG','PA','PB',
|
||||
'PR','PE','PI','RJ','RN','RS','RO','RR','SC','SP','SE','TO'
|
||||
].map(uf => ({ value: uf, label: uf }));
|
||||
|
||||
const customSocials = ref([]);
|
||||
|
||||
function addCustomSocial() {
|
||||
@@ -611,7 +634,7 @@ async function loadProfile() {
|
||||
const { data: prof, error: pErr } = await supabase
|
||||
.from('profiles')
|
||||
.select(
|
||||
'role, full_name, avatar_url, phone, bio, nickname, work_description, work_description_other, site_url, social_instagram, social_youtube, social_facebook, social_x, social_custom, language, timezone, notify_system_email, notify_reminders, notify_news'
|
||||
'role, full_name, avatar_url, phone, bio, nickname, work_description, work_description_other, site_url, social_instagram, social_youtube, social_facebook, social_x, social_custom, language, timezone, notify_system_email, notify_reminders, notify_news, professional_registration_type, professional_registration_number, professional_registration_uf'
|
||||
)
|
||||
.eq('id', user.id)
|
||||
.maybeSingle();
|
||||
@@ -631,6 +654,10 @@ async function loadProfile() {
|
||||
form.social_facebook = prof.social_facebook ?? '';
|
||||
form.social_x = prof.social_x ?? '';
|
||||
|
||||
form.professional_registration_type = prof.professional_registration_type ?? '';
|
||||
form.professional_registration_number = prof.professional_registration_number ?? '';
|
||||
form.professional_registration_uf = prof.professional_registration_uf ?? '';
|
||||
|
||||
if (Array.isArray(prof.social_custom)) {
|
||||
customSocials.value = prof.social_custom;
|
||||
}
|
||||
@@ -707,7 +734,12 @@ async function saveAll() {
|
||||
|
||||
notify_system_email: !!form.notify_system_email,
|
||||
notify_reminders: !!form.notify_reminders,
|
||||
notify_news: !!form.notify_news
|
||||
notify_news: !!form.notify_news,
|
||||
|
||||
// Registro profissional (CFP) — null se vazio
|
||||
professional_registration_type: String(form.professional_registration_type || '').trim() || null,
|
||||
professional_registration_number: String(form.professional_registration_number || '').trim() || null,
|
||||
professional_registration_uf: String(form.professional_registration_uf || '').trim() || null
|
||||
};
|
||||
|
||||
const { data: updatedProfile, error: pErr2 } = await supabase
|
||||
@@ -715,7 +747,7 @@ async function saveAll() {
|
||||
.update(profilePayload)
|
||||
.eq('id', userId.value)
|
||||
.select(
|
||||
'id, role, full_name, avatar_url, phone, bio, nickname, work_description, work_description_other, site_url, social_instagram, social_youtube, social_facebook, social_x, social_custom, language, timezone, notify_system_email, notify_reminders, notify_news, updated_at'
|
||||
'id, role, full_name, avatar_url, phone, bio, nickname, work_description, work_description_other, site_url, social_instagram, social_youtube, social_facebook, social_x, social_custom, language, timezone, notify_system_email, notify_reminders, notify_news, professional_registration_type, professional_registration_number, professional_registration_uf, updated_at'
|
||||
)
|
||||
.single();
|
||||
|
||||
@@ -1105,6 +1137,89 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── REGISTRO PROFISSIONAL (CFP #5) ─────────────────────── -->
|
||||
<div
|
||||
id="registro-profissional"
|
||||
class="pcard relative overflow-hidden rounded-md border border-[var(--surface-border)] bg-[var(--surface-card)] p-6 scroll-mt-20 transition-[border-color] duration-[220ms]"
|
||||
style="--c: #0ea5e9; --c-dim: rgba(14, 165, 233, 0.08); --c-border: rgba(14, 165, 233, 0.2)"
|
||||
>
|
||||
<div class="pcard__shine" />
|
||||
|
||||
<div class="flex items-center gap-2.5 mb-3.5">
|
||||
<div class="w-9 h-9 rounded-[0.625rem] shrink-0 flex items-center justify-center" style="background: var(--c-dim); border: 1px solid var(--c-border); color: var(--c)"><i class="pi pi-id-card" /></div>
|
||||
<span class="text-[1rem] font-extrabold uppercase tracking-[0.14em] opacity-75" style="color: var(--c)">Registro Profissional</span>
|
||||
</div>
|
||||
|
||||
<div class="text-[1rem] font-bold tracking-tight text-[var(--text-color)] mb-1">Conselho regional</div>
|
||||
<div class="text-[1rem] text-[var(--text-color-secondary)] leading-relaxed">Exigido para emissão de recibos, atestados e laudos. Aparecerá no rodapé dos documentos.</div>
|
||||
|
||||
<div class="h-px bg-[var(--surface-border)] my-5" />
|
||||
|
||||
<div class="grid grid-cols-12 gap-4">
|
||||
<!-- Tipo de conselho -->
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<FloatLabel variant="on">
|
||||
<Select
|
||||
id="prof_registration_type"
|
||||
v-model="form.professional_registration_type"
|
||||
:options="REGISTRATION_TYPE_OPTIONS"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
class="w-full"
|
||||
@update:modelValue="markDirty"
|
||||
/>
|
||||
<label for="prof_registration_type">Tipo de registro</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1.5 text-[1rem] text-[var(--text-color-secondary)]">Conselho profissional ao qual você é vinculado.</div>
|
||||
</div>
|
||||
|
||||
<!-- Número -->
|
||||
<div class="col-span-7 md:col-span-3">
|
||||
<FloatLabel variant="on">
|
||||
<InputText
|
||||
id="prof_registration_number"
|
||||
v-model="form.professional_registration_number"
|
||||
class="w-full"
|
||||
:disabled="!form.professional_registration_type"
|
||||
@input="markDirty"
|
||||
/>
|
||||
<label for="prof_registration_number">Número</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1.5 text-[1rem] text-[var(--text-color-secondary)]">Ex: 06/12345</div>
|
||||
</div>
|
||||
|
||||
<!-- UF -->
|
||||
<div class="col-span-5 md:col-span-3">
|
||||
<FloatLabel variant="on">
|
||||
<Select
|
||||
id="prof_registration_uf"
|
||||
v-model="form.professional_registration_uf"
|
||||
:options="UF_OPTIONS"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
:disabled="!form.professional_registration_type"
|
||||
class="w-full"
|
||||
:filter="true"
|
||||
@update:modelValue="markDirty"
|
||||
/>
|
||||
<label for="prof_registration_uf">UF</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1.5 text-[1rem] text-[var(--text-color-secondary)]">Estado do conselho.</div>
|
||||
</div>
|
||||
|
||||
<!-- Preview -->
|
||||
<div v-if="form.professional_registration_type && form.professional_registration_number" class="col-span-12">
|
||||
<div class="rounded-md border border-[var(--c-border)] bg-[var(--c-dim)] p-3 text-[0.9rem]">
|
||||
<span class="text-[var(--text-color-secondary)] mr-2">Aparecerá nos documentos como:</span>
|
||||
<strong class="text-[var(--text-color)]">
|
||||
{{ form.professional_registration_type }}
|
||||
{{ form.professional_registration_number }}{{ form.professional_registration_uf ? '/' + form.professional_registration_uf : '' }}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── 02 SITES E REDES SOCIAIS ─────────────────────── -->
|
||||
<div
|
||||
id="redes-sociais"
|
||||
|
||||
Reference in New Issue
Block a user