Preficicação, Convenio, Ajustes Agenda, Configurações Excessões
This commit is contained in:
@@ -494,29 +494,178 @@
|
||||
</div>
|
||||
<div v-if="isSessionEvent" class="summary-row">
|
||||
<i class="pi pi-wallet summary-icon" />
|
||||
<span>{{ form.price != null ? Number(form.price).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }) : '—' }}</span>
|
||||
<span>{{ displayPrice != null ? fmtBRL(displayPrice) : '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── ESCOPO DE EDIÇÃO — só em modo edição de série ── -->
|
||||
<div v-if="isEdit && hasSerie" class="side-card mb-3">
|
||||
<div class="side-card__title mb-2">Aplicar alterações em</div>
|
||||
<SelectButton
|
||||
v-model="editScope"
|
||||
:options="editScopeOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
class="w-full"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- ── CONVÊNIO (só sessão) ─────────────────────── -->
|
||||
<div v-if="isSessionEvent" class="side-card mb-3">
|
||||
<div class="side-card__title mb-2">Convênio</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Select
|
||||
v-model="form.insurance_plan_id"
|
||||
:options="insurancePlans"
|
||||
optionLabel="name"
|
||||
optionValue="id"
|
||||
placeholder="Particular (sem convênio)"
|
||||
showClear
|
||||
class="w-full"
|
||||
size="small"
|
||||
/>
|
||||
<template v-if="hasInsurance">
|
||||
<div class="flex gap-2">
|
||||
<div class="flex-1">
|
||||
<label class="text-xs text-color-secondary mb-1 block">Nº da Guia</label>
|
||||
<InputText
|
||||
v-model="form.insurance_guide_number"
|
||||
placeholder="Ex: 123456789"
|
||||
class="w-full"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<label class="text-xs text-color-secondary mb-1 block">Valor do Convênio (R$)</label>
|
||||
<InputNumber
|
||||
v-model="form.insurance_value"
|
||||
mode="currency"
|
||||
currency="BRL"
|
||||
locale="pt-BR"
|
||||
class="w-full"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── RECORRÊNCIA (só sessão) ───────────────────── -->
|
||||
<div v-if="isSessionEvent" class="side-card">
|
||||
|
||||
<!-- Valor da sessão -->
|
||||
<!-- Serviços / Valor da sessão -->
|
||||
<div class="mb-3">
|
||||
<FloatLabel variant="on">
|
||||
<InputNumber
|
||||
v-model="form.price"
|
||||
inputId="aed-price-side"
|
||||
mode="currency"
|
||||
currency="BRL"
|
||||
locale="pt-BR"
|
||||
:min="0"
|
||||
:max="99999"
|
||||
:minFractionDigits="2"
|
||||
fluid
|
||||
|
||||
<!-- SelectButton Gratuito/Pago -->
|
||||
<SelectButton
|
||||
v-model="billingType"
|
||||
:options="billingTypeOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
class="mb-3 w-full"
|
||||
/>
|
||||
|
||||
<!-- Seletor de serviço (só quando Pago e há serviços cadastrados) -->
|
||||
<div v-if="billingType === 'pago' && services.length" class="flex gap-2 mb-2">
|
||||
<Select
|
||||
v-model="servicePickerSel"
|
||||
:options="services"
|
||||
optionLabel="name"
|
||||
optionValue="id"
|
||||
placeholder="Adicionar serviço..."
|
||||
class="flex-1"
|
||||
size="small"
|
||||
@update:modelValue="(id) => { addItem(services.find(s => s.id === id)); servicePickerSel = null }"
|
||||
/>
|
||||
<label for="aed-price-side">Valor da sessão (R$)</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
|
||||
<!-- Lista de itens adicionados -->
|
||||
<div v-if="commitmentItems.length" class="commitment-items-list mb-2">
|
||||
<div
|
||||
v-for="(item, idx) in commitmentItems"
|
||||
:key="idx"
|
||||
class="commitment-item-row"
|
||||
>
|
||||
<!-- linha 1: nome + remover -->
|
||||
<div class="commitment-item-header">
|
||||
<span class="commitment-item-name">{{ item.service_name }}</span>
|
||||
<Button
|
||||
icon="pi pi-times"
|
||||
size="small"
|
||||
severity="danger"
|
||||
text
|
||||
@click="removeItem(idx)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- linha 2: qtd | preço unit (editável) | desconto % | desconto fixo | total -->
|
||||
<div class="commitment-item-controls">
|
||||
<div class="commitment-item-field">
|
||||
<label class="commitment-item-label">Qtd</label>
|
||||
<InputNumber
|
||||
v-model="item.quantity"
|
||||
:min="1"
|
||||
:max="99"
|
||||
size="small"
|
||||
inputClass="w-12 text-center"
|
||||
@update:modelValue="onItemChange(item)"
|
||||
/>
|
||||
</div>
|
||||
<div class="commitment-item-field">
|
||||
<label class="commitment-item-label">Preço unit.</label>
|
||||
<InputNumber
|
||||
v-model="item.unit_price"
|
||||
mode="currency"
|
||||
currency="BRL"
|
||||
locale="pt-BR"
|
||||
:min="0"
|
||||
:minFractionDigits="2"
|
||||
size="small"
|
||||
inputClass="w-24"
|
||||
@update:modelValue="onItemChange(item)"
|
||||
/>
|
||||
</div>
|
||||
<div class="commitment-item-field">
|
||||
<label class="commitment-item-label">Desc %</label>
|
||||
<InputNumber
|
||||
v-model="item.discount_pct"
|
||||
:min="0"
|
||||
:max="100"
|
||||
suffix="%"
|
||||
size="small"
|
||||
inputClass="w-16 text-center"
|
||||
@update:modelValue="onItemChange(item)"
|
||||
/>
|
||||
</div>
|
||||
<div class="commitment-item-field">
|
||||
<label class="commitment-item-label">Desc R$</label>
|
||||
<InputNumber
|
||||
v-model="item.discount_flat"
|
||||
mode="currency"
|
||||
currency="BRL"
|
||||
locale="pt-BR"
|
||||
:min="0"
|
||||
:minFractionDigits="2"
|
||||
size="small"
|
||||
inputClass="w-20"
|
||||
@update:modelValue="onItemChange(item)"
|
||||
/>
|
||||
</div>
|
||||
<div class="commitment-item-field commitment-item-field--final">
|
||||
<label class="commitment-item-label">Total</label>
|
||||
<span class="commitment-item-price">{{ fmtBRL(item.final_price) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="commitment-items-total">
|
||||
<span class="text-sm text-color-secondary">Total da sessão</span>
|
||||
<span class="font-semibold">{{ fmtBRL(totalFromItems) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Observação -->
|
||||
@@ -644,6 +793,22 @@
|
||||
<span class="text-xs">{{ totalConflitos }} sessão(ões) com conflito serão marcadas automaticamente para ajuste.</span>
|
||||
</Message>
|
||||
</div>
|
||||
|
||||
<!-- Modo de valor da série — informativo, só quando há serviços + recorrência -->
|
||||
<div v-if="recorrenciaType !== 'avulsa' && commitmentItems.length > 0" class="mb-1 mt-3">
|
||||
<label class="block text-xs font-semibold text-color-secondary mb-1.5">Como interpretar o valor</label>
|
||||
<SelectButton
|
||||
v-model="serieValorMode"
|
||||
:options="[{ label: 'Por sessão', value: 'multiplicar' }, { label: 'Pacote fechado', value: 'dividir' }]"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
size="small"
|
||||
class="w-full mb-2"
|
||||
/>
|
||||
<Message v-if="serieValorAviso" severity="info" :closable="false">
|
||||
<span class="text-xs">{{ serieValorAviso }}</span>
|
||||
</Message>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@@ -823,7 +988,11 @@
|
||||
optionGroupChildren="items"
|
||||
variant="filled"
|
||||
class="w-full"
|
||||
:disabled="isDynamic && commitmentItems.length > 0"
|
||||
/>
|
||||
<small v-if="isDynamic && commitmentItems.length > 0" class="text-color-secondary text-xs mt-1 block">
|
||||
Calculado pelos serviços adicionados
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -875,7 +1044,10 @@ import Message from 'primevue/message'
|
||||
import { useConfirm } from 'primevue/useconfirm'
|
||||
import { supabase } from '@/lib/supabase/client'
|
||||
import ComponentCadastroRapido from '@/components/ComponentCadastroRapido.vue'
|
||||
import { useProfessionalPricing } from '@/features/agenda/composables/useProfessionalPricing'
|
||||
import { useServices } from '@/features/agenda/composables/useServices'
|
||||
import { useCommitmentServices } from '@/features/agenda/composables/useCommitmentServices'
|
||||
import { usePatientDiscounts } from '@/features/agenda/composables/usePatientDiscounts'
|
||||
import { useInsurancePlans } from '@/features/agenda/composables/useInsurancePlans'
|
||||
|
||||
function patientInitials (nome) {
|
||||
const parts = String(nome || '').trim().split(/\s+/).filter(Boolean)
|
||||
@@ -942,6 +1114,7 @@ const editScopeOptions = [
|
||||
{ value: 'somente_este', label: 'Somente esta sessão' },
|
||||
{ value: 'este_e_seguintes', label: 'Esta e as seguintes' },
|
||||
{ value: 'todos', label: 'Todas da série' },
|
||||
{ value: 'todos_sem_excecao', label: 'Todas sem exceção' },
|
||||
]
|
||||
|
||||
// ── recorrência (criação / sessão avulsa) ──────────────────
|
||||
@@ -1110,24 +1283,167 @@ function isNativeSession (c) {
|
||||
|
||||
const form = ref(resetForm())
|
||||
|
||||
// ── Precificação ────────────────────────────────────────────────────
|
||||
const { getPriceFor, load: loadPricing } = useProfessionalPricing()
|
||||
let _pricingLoaded = false
|
||||
// ── Precificação / Serviços ─────────────────────────────────────────
|
||||
const { services, getDefaultPrice, load: loadServices } = useServices()
|
||||
const { loadItems: _csLoadItems, saveItems: saveCommitmentItems, loadItemsOrTemplate: _csLoadItemsOrTemplate } = useCommitmentServices()
|
||||
const { loadActive: loadActiveDiscount } = usePatientDiscounts()
|
||||
const { plans: insurancePlans, load: loadInsurancePlans } = useInsurancePlans()
|
||||
let _servicesLoaded = false
|
||||
|
||||
async function ensurePricingLoaded () {
|
||||
if (_pricingLoaded || !props.ownerId) return
|
||||
_pricingLoaded = true
|
||||
await loadPricing(props.ownerId)
|
||||
async function ensureServicesLoaded () {
|
||||
if (_servicesLoaded || !props.ownerId) return
|
||||
_servicesLoaded = true
|
||||
await loadServices(props.ownerId)
|
||||
}
|
||||
|
||||
function applyPriceForCommitment (commitmentId) {
|
||||
function applyDefaultPrice () {
|
||||
// Pula quando pago: o preço vem dos commitmentItems, não de um default
|
||||
if (billingType.value === 'pago') return
|
||||
// Só auto-preenche se price ainda não foi definido manualmente (ou é novo evento)
|
||||
if (!isEdit.value) {
|
||||
const suggested = getPriceFor(commitmentId)
|
||||
const suggested = getDefaultPrice()
|
||||
if (suggested != null) form.value.price = suggested
|
||||
}
|
||||
}
|
||||
|
||||
// ── Itens de serviço (commitment_services) ──────────────────────────
|
||||
const commitmentItems = ref([])
|
||||
const servicePickerSel = ref(null)
|
||||
const serieValorMode = ref('multiplicar') // 'multiplicar' | 'dividir'
|
||||
|
||||
const billingType = ref('pago') // 'gratuito' | 'pago'
|
||||
const billingTypeOptions = [
|
||||
{ label: 'Gratuito', value: 'gratuito' },
|
||||
{ label: 'Pago', value: 'pago' },
|
||||
]
|
||||
|
||||
watch(billingType, (val) => {
|
||||
if (val === 'gratuito') {
|
||||
commitmentItems.value = []
|
||||
form.value.price = 0
|
||||
}
|
||||
})
|
||||
|
||||
const isDynamic = computed(() =>
|
||||
(props.agendaSettings?.slot_mode ?? 'fixed') === 'dynamic'
|
||||
)
|
||||
|
||||
const totalFromItems = computed(() =>
|
||||
commitmentItems.value.reduce((sum, item) => sum + (item.final_price ?? 0), 0)
|
||||
)
|
||||
|
||||
// Duração calculada como soma de services.duration_min dos itens (slot_mode=dynamic)
|
||||
const dynamicDuration = computed(() => {
|
||||
if (!isDynamic.value) return null
|
||||
return commitmentItems.value.reduce((sum, item) => {
|
||||
const svc = services.value.find(s => s.id === item.service_id)
|
||||
return sum + (svc?.duration_min ?? 0)
|
||||
}, 0)
|
||||
})
|
||||
|
||||
// Preço exibido no resumo: total dos itens quando há itens, form.price caso contrário
|
||||
const displayPrice = computed(() =>
|
||||
commitmentItems.value.length > 0 ? totalFromItems.value : form.value.price
|
||||
)
|
||||
|
||||
// Aviso informativo de valor total da série (não altera os valores gravados)
|
||||
const serieValorAviso = computed(() => {
|
||||
if (recorrenciaType.value === 'avulsa' || !commitmentItems.value.length) return null
|
||||
const n = qtdSessoesEfetiva.value
|
||||
if (!n || !totalFromItems.value) return null
|
||||
if (serieValorMode.value === 'multiplicar') {
|
||||
return `Total da série: ${fmtBRL(totalFromItems.value * n)} (${fmtBRL(totalFromItems.value)} × ${n} sessões)`
|
||||
}
|
||||
return `Valor por sessão: ${fmtBRL(totalFromItems.value / n)} (${fmtBRL(totalFromItems.value)} ÷ ${n} sessões)`
|
||||
})
|
||||
|
||||
// Sync: total dos itens → form.price
|
||||
watch(totalFromItems, (total) => {
|
||||
if (commitmentItems.value.length > 0) form.value.price = total
|
||||
})
|
||||
|
||||
// Sync: duração dinâmica → form.duracaoMin (slot_mode=dynamic)
|
||||
watch(dynamicDuration, (dur) => {
|
||||
if (isDynamic.value && dur != null && dur > 0) form.value.duracaoMin = dur
|
||||
})
|
||||
|
||||
function calcFinalPrice (unit_price, quantity, discount_pct, discount_flat) {
|
||||
const subtotal = Number(unit_price) * Number(quantity)
|
||||
const discPct = subtotal * (Number(discount_pct ?? 0) / 100)
|
||||
const discFlat = Number(discount_flat ?? 0)
|
||||
return Math.max(0, subtotal - discPct - discFlat)
|
||||
}
|
||||
|
||||
async function addItem (svc) {
|
||||
if (!svc?.id) return
|
||||
// Regra: não duplicar — incrementa quantity do item existente
|
||||
const existing = commitmentItems.value.find(i => i.service_id === svc.id)
|
||||
if (existing) {
|
||||
existing.quantity++
|
||||
existing.final_price = calcFinalPrice(
|
||||
existing.unit_price, existing.quantity, existing.discount_pct, existing.discount_flat
|
||||
)
|
||||
return
|
||||
}
|
||||
const unit_price = Number(svc.price)
|
||||
const patientId = form.value.patient_id ?? form.value.paciente_id ?? null
|
||||
let discount_pct = 0
|
||||
let discount_flat = 0
|
||||
|
||||
if (patientId && props.ownerId) {
|
||||
const discount = await loadActiveDiscount(props.ownerId, patientId)
|
||||
if (discount) {
|
||||
discount_pct = Number(discount.discount_pct ?? 0)
|
||||
discount_flat = Number(discount.discount_flat ?? 0)
|
||||
}
|
||||
}
|
||||
|
||||
commitmentItems.value.push({
|
||||
service_id: svc.id,
|
||||
service_name: svc.name,
|
||||
quantity: 1,
|
||||
unit_price,
|
||||
discount_pct,
|
||||
discount_flat,
|
||||
final_price: calcFinalPrice(unit_price, 1, discount_pct, discount_flat),
|
||||
})
|
||||
}
|
||||
|
||||
function removeItem (index) {
|
||||
commitmentItems.value.splice(index, 1)
|
||||
// Quando lista esvazia em modo dynamic, restaura duração padrão
|
||||
if (commitmentItems.value.length === 0 && isDynamic.value) {
|
||||
form.value.duracaoMin = props.agendaSettings?.session_duration_min ?? 50
|
||||
}
|
||||
}
|
||||
|
||||
function onItemChange (item) {
|
||||
item.final_price = calcFinalPrice(
|
||||
item.unit_price, item.quantity, item.discount_pct, item.discount_flat
|
||||
)
|
||||
}
|
||||
|
||||
async function _loadCommitmentItemsForEvent (eventId) {
|
||||
const ruleId = props.eventRow?.recurrence_id ?? null
|
||||
const isCustomized = props.eventRow?.services_customized ?? false
|
||||
if (!eventId && !ruleId) { commitmentItems.value = []; billingType.value = 'gratuito'; return }
|
||||
try {
|
||||
commitmentItems.value = ruleId
|
||||
? await _csLoadItemsOrTemplate(eventId, ruleId, { allowEmpty: isCustomized })
|
||||
: await _csLoadItems(eventId)
|
||||
billingType.value = commitmentItems.value.length > 0 ? 'pago' : 'gratuito'
|
||||
} catch (e) {
|
||||
console.warn('[AgendaEventDialog] commitment_services load error:', e?.message)
|
||||
commitmentItems.value = []
|
||||
billingType.value = 'gratuito'
|
||||
}
|
||||
}
|
||||
|
||||
function fmtBRL (v) {
|
||||
if (v == null) return '—'
|
||||
return Number(v).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' })
|
||||
}
|
||||
|
||||
const selectedCommitment = computed(() => {
|
||||
const id = form.value.commitment_id
|
||||
if (!id) return null
|
||||
@@ -1143,6 +1459,7 @@ const requiresPatient = computed(() => isNativeSession(selectedCommitment.value)
|
||||
const isSessionEvent = computed(() => requiresPatient.value)
|
||||
// Bloqueia troca de paciente quando editando sessão que já tinha paciente vinculado
|
||||
const patientLocked = computed(() => isEdit.value && isSessionEvent.value && !!(props.eventRow?.paciente_id))
|
||||
const hasInsurance = computed(() => !!form.value.insurance_plan_id)
|
||||
|
||||
// ── jornada ────────────────────────────────────────────────
|
||||
function _fmtH (hhmm) {
|
||||
@@ -1329,6 +1646,7 @@ watch(
|
||||
qtdSessoesMode.value = '4'
|
||||
qtdSessoesCustom.value = 12
|
||||
editScope.value = 'somente_este'
|
||||
serieValorMode.value = 'multiplicar'
|
||||
|
||||
if (isEdit.value && form.value.paciente_id && !form.value.paciente_nome) {
|
||||
supabase
|
||||
@@ -1355,8 +1673,26 @@ watch(
|
||||
clearPatientsCache()
|
||||
if (requiresPatient.value) loadPatients(true)
|
||||
|
||||
// Pré-carrega precificação para auto-fill
|
||||
ensurePricingLoaded()
|
||||
// Pré-carrega serviços para auto-fill de preço
|
||||
ensureServicesLoaded()
|
||||
if (props.ownerId) {
|
||||
await loadInsurancePlans(props.ownerId)
|
||||
// Se já tem convênio selecionado (edição), aplica o valor padrão agora que os planos estão carregados
|
||||
const planId = form.value.insurance_plan_id
|
||||
if (planId && !form.value.insurance_value) {
|
||||
const plan = insurancePlans.value.find(p => p.id === planId)
|
||||
if (plan?.default_value) form.value.insurance_value = plan.default_value
|
||||
}
|
||||
}
|
||||
|
||||
// Reset e carrega itens de serviço do evento (commitment_services)
|
||||
commitmentItems.value = []
|
||||
servicePickerSel.value = null
|
||||
billingType.value = 'pago'
|
||||
// Carrega serviços para eventos reais (form.value.id) ou template para ocorrências virtuais (só recurrence_id)
|
||||
if (isEdit.value && (form.value.id || props.eventRow?.recurrence_id)) {
|
||||
_loadCommitmentItemsForEvent(form.value.id)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1374,8 +1710,17 @@ watch(
|
||||
() => form.value.commitment_id,
|
||||
async (newId) => {
|
||||
if (!newId || isEdit.value || !visible.value) return
|
||||
await ensurePricingLoaded()
|
||||
applyPriceForCommitment(newId)
|
||||
await ensureServicesLoaded()
|
||||
applyDefaultPrice()
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => form.value.insurance_plan_id,
|
||||
(planId) => {
|
||||
if (!planId) { form.value.insurance_value = null; return }
|
||||
const plan = insurancePlans.value.find(p => p.id === planId)
|
||||
if (plan?.default_value) form.value.insurance_value = plan.default_value
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1915,6 +2260,7 @@ const canSave = computed(() => {
|
||||
if (!form.value.startTime) return false
|
||||
if (!form.value.commitment_id) return false
|
||||
if (requiresPatient.value && !form.value.paciente_id) return false
|
||||
if (isSessionEvent.value && billingType.value === 'pago' && commitmentItems.value.length === 0) return false
|
||||
return true
|
||||
})
|
||||
|
||||
@@ -1948,6 +2294,9 @@ function onSave () {
|
||||
titulo_custom: form.value.titulo_custom || null,
|
||||
extra_fields: Object.keys(form.value.extra_fields || {}).length ? form.value.extra_fields : null,
|
||||
price: isSessionEvent.value ? (form.value.price ?? null) : null,
|
||||
insurance_plan_id: isSessionEvent.value ? (form.value.insurance_plan_id ?? null) : null,
|
||||
insurance_guide_number: isSessionEvent.value ? (form.value.insurance_guide_number ?? null) : null,
|
||||
insurance_value: isSessionEvent.value ? (form.value.insurance_value ?? null) : null,
|
||||
}
|
||||
|
||||
// recorrência — só quando é sessão e não avulsa
|
||||
@@ -1962,6 +2311,8 @@ function onSave () {
|
||||
duracaoMin: form.value.duracaoMin,
|
||||
dataFim: dataFimCalculada.value ? dataFimCalculada.value.toISOString() : null,
|
||||
qtdSessoes: qtdSessoesEfetiva.value,
|
||||
serieValorMode: serieValorMode.value,
|
||||
commitmentItems: commitmentItems.value.slice(),
|
||||
}
|
||||
recorrencia.conflitos = ocorrenciasComConflito.value
|
||||
.filter(o => o.conflict)
|
||||
@@ -1984,6 +2335,12 @@ function onSave () {
|
||||
original_date: emitOriginalDate,
|
||||
// legado — mantido para compatibilidade
|
||||
serie_id: props.eventRow?.serie_id ?? null,
|
||||
serviceItems: isSessionEvent.value ? commitmentItems.value.slice() : null,
|
||||
onSaved: isSessionEvent.value
|
||||
? async (eventId, { markCustomized = false } = {}) => {
|
||||
await saveCommitmentItems(eventId, commitmentItems.value, { markCustomized })
|
||||
}
|
||||
: null,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2121,6 +2478,9 @@ function resetForm () {
|
||||
conflito: null,
|
||||
extra_fields: r?.extra_fields && typeof r.extra_fields === 'object' ? { ...r.extra_fields } : {},
|
||||
price: r?.price != null ? Number(r.price) : null,
|
||||
insurance_plan_id: r?.insurance_plan_id ?? null,
|
||||
insurance_guide_number: r?.insurance_guide_number ?? null,
|
||||
insurance_value: r?.insurance_value != null ? Number(r.insurance_value) : null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2762,4 +3122,68 @@ function statusSeverity (v) {
|
||||
border-radius: 999px; padding: .1rem .45rem; flex-shrink: 0; white-space: nowrap;
|
||||
}
|
||||
.serie-pill__del { flex-shrink: 0; width: 2rem; }
|
||||
|
||||
/* ── Commitment items (serviços vinculados ao evento) ── */
|
||||
.commitment-items-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .35rem;
|
||||
border: 1px solid var(--surface-border);
|
||||
border-radius: .5rem;
|
||||
padding: .5rem;
|
||||
}
|
||||
.commitment-item-row {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.commitment-item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.commitment-item-name {
|
||||
flex: 1;
|
||||
font-size: .85rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.commitment-item-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: flex-end;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--p-content-border-color);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.commitment-item-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.commitment-item-field--final {
|
||||
margin-left: auto;
|
||||
}
|
||||
.commitment-item-label {
|
||||
font-size: 0.65rem;
|
||||
color: var(--p-text-muted-color);
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
.commitment-item-price {
|
||||
font-size: .85rem;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
min-width: 5rem;
|
||||
text-align: right;
|
||||
}
|
||||
.commitment-items-total {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-top: .35rem;
|
||||
margin-top: .25rem;
|
||||
border-top: 1px solid var(--surface-border);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user