WIP jordan
This commit is contained in:
417
app/pages/registration/client.vue
Normal file
417
app/pages/registration/client.vue
Normal file
@@ -0,0 +1,417 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectItem } from '@nuxt/ui'
|
||||
import type { ClientRegistrationNatural } from '~/types/brokerage-registration'
|
||||
import {
|
||||
createEmptyClientRegistration,
|
||||
toIndividualCustomerBody,
|
||||
useClientCaptureMeta
|
||||
} from '~/composables/useClientRegistrationModel'
|
||||
|
||||
definePageMeta({ ssr: false })
|
||||
usePageTitle('Client registration')
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const submitting = ref(false)
|
||||
const toast = useToast()
|
||||
const { $customer } = useNuxtApp()
|
||||
|
||||
/* ── Success state ── */
|
||||
const createdCustomerId = ref<string | null>(null)
|
||||
const createdCustomerName = ref('')
|
||||
const showSuccess = computed(() => !!createdCustomerId.value)
|
||||
|
||||
const customerType = ref<'individual' | 'corporate'>(
|
||||
route.query.type === 'corporate' ? 'corporate' : 'individual'
|
||||
)
|
||||
|
||||
const form = ref<ClientRegistrationNatural>(createEmptyClientRegistration())
|
||||
const captureMeta = ref(useClientCaptureMeta())
|
||||
|
||||
const corporateForm = ref({
|
||||
legal_name: '',
|
||||
commercial_name: '',
|
||||
ruc: '',
|
||||
legal_rep_name: '',
|
||||
legal_rep_document_id: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
address: ''
|
||||
})
|
||||
|
||||
const idTypeItems = ref<SelectItem[]>([
|
||||
{ label: 'Cédula', value: 'cedula' },
|
||||
{ label: 'Pasaporte', value: 'pasaporte' },
|
||||
{ label: 'RUC', value: 'ruc' }
|
||||
])
|
||||
|
||||
const isValidIndividual = computed(() => {
|
||||
const f = form.value
|
||||
return (
|
||||
!!f.primerNombre.trim() &&
|
||||
!!f.apellidoPaterno.trim() &&
|
||||
!!f.correoElectronicoPersonal.trim() &&
|
||||
!!f.cedulaOPasaporte.trim()
|
||||
)
|
||||
})
|
||||
|
||||
const isValidCorporate = computed(() => corporateForm.value.legal_name && corporateForm.value.ruc)
|
||||
|
||||
async function submitIndividual() {
|
||||
submitting.value = true
|
||||
try {
|
||||
const body = toIndividualCustomerBody(form.value)
|
||||
const data = (await $customer('/customers', {
|
||||
method: 'POST',
|
||||
body
|
||||
})) as { data?: { id: string } }
|
||||
toast.add({ title: 'Customer created', color: 'success' })
|
||||
createdCustomerName.value = `${form.value.primerNombre} ${form.value.apellidoPaterno}`.trim()
|
||||
createdCustomerId.value = data?.data?.id ?? 'new'
|
||||
} catch (e: unknown) {
|
||||
const err = e as { data?: { errors?: unknown }; message?: string }
|
||||
toast.add({
|
||||
title: 'Failed to create customer',
|
||||
description: err?.data?.errors ? JSON.stringify(err.data.errors) : err?.message,
|
||||
color: 'error'
|
||||
})
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function submitCorporate() {
|
||||
submitting.value = true
|
||||
try {
|
||||
const data = (await $customer('/customers/corporate', {
|
||||
method: 'POST',
|
||||
body: corporateForm.value
|
||||
})) as { data?: { id: string } }
|
||||
toast.add({ title: 'Corporate customer created', color: 'success' })
|
||||
createdCustomerName.value = corporateForm.value.legal_name || corporateForm.value.commercial_name
|
||||
createdCustomerId.value = data?.data?.id ?? 'new'
|
||||
} catch (e: unknown) {
|
||||
const err = e as { data?: { errors?: unknown }; message?: string }
|
||||
toast.add({
|
||||
title: 'Failed to create customer',
|
||||
description: err?.data?.errors ? JSON.stringify(err.data.errors) : err?.message,
|
||||
color: 'error'
|
||||
})
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function resetAndAddAnother() {
|
||||
createdCustomerId.value = null
|
||||
createdCustomerName.value = ''
|
||||
form.value = createEmptyClientRegistration()
|
||||
corporateForm.value = {
|
||||
legal_name: '', commercial_name: '', ruc: '',
|
||||
legal_rep_name: '', legal_rep_document_id: '',
|
||||
email: '', phone: '', address: ''
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="nc mx-auto max-w-5xl space-y-6 pb-12">
|
||||
<!-- Back -->
|
||||
<NuxtLink to="/customers" class="inline-flex">
|
||||
<UButton color="neutral" variant="ghost" size="sm" icon="i-heroicons-arrow-left">Customers</UButton>
|
||||
</NuxtLink>
|
||||
|
||||
<!-- Sales flow indicator -->
|
||||
<SalesFlowIndicator current-stage="customer" />
|
||||
|
||||
<div class="flex flex-wrap items-start justify-between gap-4">
|
||||
<div>
|
||||
<h1 class="mt-1 text-2xl font-semibold tracking-tight text-[var(--text-primary)]">New Customer Registration</h1>
|
||||
<p class="mt-1 text-[13px] text-[var(--text-muted)]">
|
||||
Extended capture aligned with brokerage intake. Core fields map to the customer API.
|
||||
</p>
|
||||
</div>
|
||||
<div class="nc-meta-card">
|
||||
<div class="nc-meta-row">
|
||||
<span class="nc-meta-label">Operator</span>
|
||||
<span class="nc-meta-value">{{ captureMeta.operadorNombre }}</span>
|
||||
</div>
|
||||
<div class="nc-meta-row">
|
||||
<span class="nc-meta-label">Progress</span>
|
||||
<span class="nc-meta-value">{{ captureMeta.progresoCapturaPct }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Type toggle -->
|
||||
<div class="nc-tabs">
|
||||
<button
|
||||
v-for="type in [
|
||||
{ id: 'individual' as const, label: 'Persona natural', icon: 'i-heroicons-user' },
|
||||
{ id: 'corporate' as const, label: 'Persona jurídica', icon: 'i-heroicons-building-office' }
|
||||
]"
|
||||
:key="type.id"
|
||||
type="button"
|
||||
class="nc-tab"
|
||||
:class="customerType === type.id ? 'nc-tab-on' : 'nc-tab-off'"
|
||||
@click="customerType = type.id"
|
||||
>
|
||||
<UIcon :name="type.icon" style="width: 14px; height: 14px;" />
|
||||
{{ type.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ═══ Success state ═══ -->
|
||||
<div v-if="showSuccess" class="nc-success">
|
||||
<div class="nc-success-icon">
|
||||
<UIcon name="i-heroicons-check-circle" style="width: 32px; height: 32px;" />
|
||||
</div>
|
||||
<h2 class="mt-4 text-[17px] font-semibold text-[var(--text-primary)]">Customer registered</h2>
|
||||
<p class="mt-1 text-[13px] text-[var(--text-muted)]">
|
||||
<strong class="font-medium text-[var(--text-primary)]">{{ createdCustomerName }}</strong> has been added to your customer base.
|
||||
</p>
|
||||
<p class="mt-1 text-[12px] text-[var(--text-muted)]">What would you like to do next?</p>
|
||||
|
||||
<div class="nc-success-actions">
|
||||
<NuxtLink :to="`/quotes/new?customer=${createdCustomerId}`" class="nc-success-btn nc-success-primary">
|
||||
<UIcon name="i-heroicons-calculator" style="width: 16px; height: 16px;" />
|
||||
Proceed to quote
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="`/customers/${createdCustomerId}`" class="nc-success-btn nc-success-secondary">
|
||||
<UIcon name="i-heroicons-user" style="width: 16px; height: 16px;" />
|
||||
View profile
|
||||
</NuxtLink>
|
||||
<button type="button" class="nc-success-btn nc-success-secondary" @click="resetAndAddAnother">
|
||||
<UIcon name="i-heroicons-plus" style="width: 16px; height: 16px;" />
|
||||
Register another
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!showSuccess && customerType === 'individual'" class="nc-card">
|
||||
<div class="nc-card-head">
|
||||
<UIcon name="i-heroicons-identification" style="width: 16px; height: 16px; color: #01696f;" />
|
||||
<span>Datos del cliente</span>
|
||||
</div>
|
||||
<div class="nc-card-body space-y-6">
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
||||
<UFormField label="ID (Mint)">
|
||||
<UInput v-model="form.id" placeholder="Auto" disabled class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Grupo económico">
|
||||
<UInput v-model="form.economicGroupId" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Conglomerado">
|
||||
<UInput v-model="form.conglomerateId" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<UFormField label="Apellido paterno" required>
|
||||
<UInput v-model="form.apellidoPaterno" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Apellido materno">
|
||||
<UInput v-model="form.apellidoMaterno" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Primer nombre" required>
|
||||
<UInput v-model="form.primerNombre" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Segundo nombre">
|
||||
<UInput v-model="form.segundoNombre" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Fecha de nacimiento">
|
||||
<UInput v-model="form.fechaNacimiento" type="date" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Tipo de identificación">
|
||||
<USelect v-model="form.tipoIdentificacion" :items="idTypeItems" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Cédula / pasaporte" required>
|
||||
<UInput v-model="form.cedulaOPasaporte" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Teléfono celular">
|
||||
<UInput v-model="form.telefonoCelular" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Correo electrónico" required>
|
||||
<UInput v-model="form.correoElectronicoPersonal" type="email" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Ocupación">
|
||||
<UInput v-model="form.ocupacion" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<UFormField label="Procedencia">
|
||||
<UInput v-model="form.procedencia" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Detalle">
|
||||
<UTextarea v-model="form.detalle" :rows="2" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Descripción">
|
||||
<UTextarea v-model="form.descripcion" :rows="3" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nc-card-footer">
|
||||
<NuxtLink to="/customers">
|
||||
<UButton color="neutral" variant="soft" size="sm">Cancel</UButton>
|
||||
</NuxtLink>
|
||||
<UButton
|
||||
color="primary"
|
||||
icon="i-heroicons-check"
|
||||
size="sm"
|
||||
:loading="submitting"
|
||||
:disabled="!isValidIndividual"
|
||||
@click="submitIndividual"
|
||||
>
|
||||
Guardar cliente
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!showSuccess && customerType === 'corporate'" class="nc-card">
|
||||
<div class="nc-card-head">
|
||||
<UIcon name="i-heroicons-building-office" style="width: 16px; height: 16px; color: #01696f;" />
|
||||
<span>Persona jurídica</span>
|
||||
</div>
|
||||
<div class="nc-card-body space-y-4">
|
||||
<UFormField label="Legal name" required>
|
||||
<UInput v-model="corporateForm.legal_name" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Commercial name">
|
||||
<UInput v-model="corporateForm.commercial_name" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="RUC" required>
|
||||
<UInput v-model="corporateForm.ruc" class="w-full" />
|
||||
</UFormField>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<UFormField label="Legal representative">
|
||||
<UInput v-model="corporateForm.legal_rep_name" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Legal rep document ID">
|
||||
<UInput v-model="corporateForm.legal_rep_document_id" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
<UFormField label="Email">
|
||||
<UInput v-model="corporateForm.email" type="email" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Phone">
|
||||
<UInput v-model="corporateForm.phone" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Address">
|
||||
<UInput v-model="corporateForm.address" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
<div class="nc-card-footer">
|
||||
<NuxtLink to="/customers">
|
||||
<UButton color="neutral" variant="soft" size="sm">Cancel</UButton>
|
||||
</NuxtLink>
|
||||
<UButton
|
||||
color="primary"
|
||||
icon="i-heroicons-check"
|
||||
size="sm"
|
||||
:loading="submitting"
|
||||
:disabled="!isValidCorporate"
|
||||
@click="submitCorporate"
|
||||
>
|
||||
Create corporate customer
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.nc-section-label {
|
||||
font-size: 11px; font-weight: 600; text-transform: uppercase;
|
||||
letter-spacing: 0.06em; color: #8a8a86; margin-bottom: 4px;
|
||||
}
|
||||
.nc-meta-card {
|
||||
padding: 10px 16px; border-radius: 10px;
|
||||
border: 1px solid rgba(0,0,0,0.06); background: #ffffff;
|
||||
font-size: 12px;
|
||||
}
|
||||
.nc-meta-row { display: flex; gap: 12px; padding: 2px 0; }
|
||||
.nc-meta-label { color: #8a8a86; min-width: 60px; }
|
||||
.nc-meta-value { color: var(--text-primary); font-weight: 500; }
|
||||
|
||||
.nc-tabs {
|
||||
display: inline-flex; gap: 2px; padding: 3px;
|
||||
border-radius: 10px; background: rgba(0,0,0,0.04);
|
||||
}
|
||||
.nc-tab {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 7px 16px; border-radius: 8px;
|
||||
font-size: 13px; font-weight: 500;
|
||||
border: none; cursor: pointer; transition: all 150ms ease;
|
||||
}
|
||||
.nc-tab-on { background: #fff; color: var(--text-primary); box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
|
||||
.nc-tab-off { background: transparent; color: var(--text-muted); }
|
||||
.nc-tab-off:hover { color: var(--text-primary); }
|
||||
|
||||
.nc-card {
|
||||
border-radius: 12px; border: 1px solid rgba(0,0,0,0.06);
|
||||
background: #ffffff; box-shadow: 0 1px 3px rgba(0,0,0,0.03);
|
||||
overflow: hidden;
|
||||
}
|
||||
.nc-card-head {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 14px 20px; border-bottom: 1px solid rgba(0,0,0,0.06);
|
||||
font-size: 13px; font-weight: 600; color: var(--text-primary);
|
||||
}
|
||||
.nc-card-body { padding: 20px; }
|
||||
.nc-card-footer {
|
||||
display: flex; justify-content: flex-end; gap: 8px;
|
||||
padding: 14px 20px; border-top: 1px solid rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
/* ── Success state ── */
|
||||
.nc-success {
|
||||
padding: 48px 24px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(1,105,111,0.12);
|
||||
background: rgba(1,105,111,0.02);
|
||||
text-align: center;
|
||||
}
|
||||
.nc-success-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 56px; height: 56px;
|
||||
border-radius: 12px;
|
||||
background: rgba(1,105,111,0.08);
|
||||
color: #01696f;
|
||||
}
|
||||
.nc-success-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
.nc-success-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 10px 18px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 150ms ease;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
border: none;
|
||||
}
|
||||
.nc-success-primary {
|
||||
background: #01696f;
|
||||
color: #fff;
|
||||
}
|
||||
.nc-success-primary:hover { background: #015458; }
|
||||
.nc-success-secondary {
|
||||
background: #fff;
|
||||
color: var(--text-primary);
|
||||
border: 1px solid rgba(0,0,0,0.1);
|
||||
}
|
||||
.nc-success-secondary:hover {
|
||||
border-color: rgba(0,0,0,0.2);
|
||||
background: rgba(0,0,0,0.02);
|
||||
}
|
||||
</style>
|
||||
234
app/pages/registration/policy.vue
Normal file
234
app/pages/registration/policy.vue
Normal file
@@ -0,0 +1,234 @@
|
||||
<script setup lang="ts">
|
||||
import type { PolicyRegistration } from '~/types/brokerage-registration'
|
||||
import { POLICY_DRAFT_STORAGE_KEY } from '~/types/brokerage-registration'
|
||||
import {
|
||||
createEmptyPolicyRegistration,
|
||||
rebuildInstallmentSchedule,
|
||||
setFinOneYearAfterInicio,
|
||||
usePolicyDraftPersistence
|
||||
} from '~/composables/usePolicyRegistrationModel'
|
||||
|
||||
definePageMeta({ ssr: false })
|
||||
usePageTitle('Policy registration')
|
||||
|
||||
const toast = useToast()
|
||||
const form = ref<PolicyRegistration>(createEmptyPolicyRegistration())
|
||||
usePolicyDraftPersistence(form)
|
||||
|
||||
watch(
|
||||
() => form.value.inicioVigencia,
|
||||
() => setFinOneYearAfterInicio(form.value)
|
||||
)
|
||||
|
||||
function refreshSchedule() {
|
||||
form.value.cuotas = rebuildInstallmentSchedule(form.value)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [form.value.numCuotas, form.value.primaBruta, form.value.inicioVigencia] as const,
|
||||
() => refreshSchedule(),
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
refreshSchedule()
|
||||
})
|
||||
|
||||
const draftSavedAt = ref<string | null>(null)
|
||||
|
||||
function saveDraft() {
|
||||
try {
|
||||
localStorage.setItem(POLICY_DRAFT_STORAGE_KEY, JSON.stringify(form.value))
|
||||
draftSavedAt.value = new Date().toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })
|
||||
toast.add({ title: 'Borrador guardado', color: 'success' })
|
||||
} catch {
|
||||
toast.add({ title: 'No se pudo guardar', color: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
function clearDraft() {
|
||||
form.value = createEmptyPolicyRegistration()
|
||||
refreshSchedule()
|
||||
try {
|
||||
localStorage.removeItem(POLICY_DRAFT_STORAGE_KEY)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
toast.add({ title: 'Borrador borrado', color: 'success' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen space-y-8 bg-gray-50 p-8">
|
||||
<div class="flex flex-wrap items-start justify-between gap-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<NuxtLink to="/policies">
|
||||
<UButton icon="i-heroicons-arrow-left" color="neutral" variant="ghost">Back</UButton>
|
||||
</NuxtLink>
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold tracking-tight text-[var(--text-primary)]">Nuevo registro · Póliza</h1>
|
||||
<p class="text-[13px] text-[var(--text-muted)]">
|
||||
Condiciones particulares, plan de pagos y referencias. Draft persists in this browser until an API is
|
||||
wired.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton color="neutral" variant="soft" @click="saveDraft">Guardar borrador</UButton>
|
||||
<span v-if="draftSavedAt" class="text-[11px] text-emerald-600 font-medium">
|
||||
<UIcon name="i-heroicons-check-circle" style="width: 13px; height: 13px; vertical-align: -2px;" /> Saved at {{ draftSavedAt }}
|
||||
</span>
|
||||
<UButton color="neutral" variant="ghost" @click="clearDraft">Limpiar</UButton>
|
||||
<NuxtLink to="/onboarding/policy-upload/new">
|
||||
<UButton color="primary" variant="soft">Manual policy upload</UButton>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UCard class="max-w-5xl">
|
||||
<template #header>
|
||||
<span class="font-semibold text-[var(--text-primary)]">Identificación y ramo</span>
|
||||
</template>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<UFormField label="Número póliza Mint">
|
||||
<UInput v-model="form.mintPolicyNumber" disabled class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="ID contratante">
|
||||
<UInput v-model="form.contratanteId" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Ramo">
|
||||
<UInput v-model="form.ramo" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Sub ramo">
|
||||
<UInput v-model="form.subRamo" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Aseguradora">
|
||||
<UInput v-model="form.aseguradora" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Producto">
|
||||
<UInput v-model="form.producto" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Agencia">
|
||||
<UInput v-model="form.agencia" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Nº póliza proveedor">
|
||||
<UInput v-model="form.numeroPolizaProveedor" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Acreedor">
|
||||
<UInput v-model="form.acreedor" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard class="max-w-5xl">
|
||||
<template #header>
|
||||
<span class="font-semibold text-[var(--text-primary)]">Vigencia y comisiones</span>
|
||||
</template>
|
||||
<div class="mb-6 grid grid-cols-1 gap-4 sm:grid-cols-3">
|
||||
<UFormField label="Fecha emisión">
|
||||
<UInput v-model="form.fechaEmision" type="datetime-local" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Inicio vigencia">
|
||||
<UInput v-model="form.inicioVigencia" type="datetime-local" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Fin vigencia">
|
||||
<UInput v-model="form.finVigencia" type="datetime-local" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
<p class="mb-2 text-xs text-[var(--text-muted)]">Comisiones agente (%)</p>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
||||
<div v-for="row in form.comisiones" :key="row.idx" class="flex items-end gap-2">
|
||||
<UFormField :label="`Agente ${row.idx}`" class="flex-1">
|
||||
<UInput v-model="row.agenteId" placeholder="ID" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="%" class="w-24">
|
||||
<UInput v-model="row.porcentaje" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard class="max-w-5xl">
|
||||
<template #header>
|
||||
<span class="font-semibold text-[var(--text-primary)]">Primas y forma de pago</span>
|
||||
</template>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<UFormField label="Forma de pago">
|
||||
<UInput v-model="form.formaPago" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Valor asegurado">
|
||||
<UInput v-model="form.valorAsegurado" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Prima bruta">
|
||||
<UInput v-model="form.primaBruta" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Impuesto %">
|
||||
<UInput v-model="form.impuestoPct" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Prima neta">
|
||||
<UInput v-model="form.primaNeta" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="Número de cuotas">
|
||||
<UInput v-model.number="form.numCuotas" type="number" min="1" max="60" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard class="max-w-5xl">
|
||||
<template #header>
|
||||
<div class="flex w-full items-center justify-between gap-4">
|
||||
<span class="font-semibold text-[var(--text-primary)]">Plan de pagos</span>
|
||||
<UButton size="xs" color="neutral" variant="soft" @click="refreshSchedule">Regenerar cuotas</UButton>
|
||||
</div>
|
||||
</template>
|
||||
<div class="overflow-x-auto rounded-lg border border-[var(--card-border)] bg-[var(--surface)]">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-[var(--card-border)] bg-[var(--surface)] text-left text-[var(--text-muted)]">
|
||||
<th class="px-3 py-2 font-medium">#</th>
|
||||
<th class="px-3 py-2 font-medium">Fecha vencimiento</th>
|
||||
<th class="px-3 py-2 font-medium">Prima</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="row in form.cuotas"
|
||||
:key="row.n"
|
||||
class="border-b border-[var(--divider)] last:border-0"
|
||||
>
|
||||
<td class="px-3 py-2 text-[var(--text-muted)]">{{ row.n }}</td>
|
||||
<td class="px-3 py-2">
|
||||
<UInput v-model="row.fechaVencimiento" type="datetime-local" size="sm" />
|
||||
</td>
|
||||
<td class="px-3 py-2">
|
||||
<UInput v-model="row.prima" size="sm" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard class="max-w-5xl">
|
||||
<template #header>
|
||||
<span class="font-semibold text-[var(--text-primary)]">Referencias y notas</span>
|
||||
</template>
|
||||
<div class="space-y-4">
|
||||
<UFormField label="Cotización Mint ID">
|
||||
<UInput v-model="form.cotizacionMintId" class="w-full" />
|
||||
</UFormField>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<UFormField label="PDF cotización (nombre)">
|
||||
<UInput v-model="form.pdfCotizacionNombre" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField label="PDF póliza (nombre)">
|
||||
<UInput v-model="form.pdfPolizaNombre" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
<UFormField label="Notas">
|
||||
<UTextarea v-model="form.notas" :rows="4" class="w-full" />
|
||||
</UFormField>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user