WIP jordan
This commit is contained in:
329
app/pages/workstation/collectivos.vue
Normal file
329
app/pages/workstation/collectivos.vue
Normal file
@@ -0,0 +1,329 @@
|
||||
<script setup lang="ts">
|
||||
usePageTitle('Collectivos')
|
||||
|
||||
const activeTab = ref<'groups' | 'census' | 'billing'>('groups')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mx-auto max-w-5xl space-y-6 pb-12">
|
||||
<!-- Header -->
|
||||
<div class="max-w-2xl">
|
||||
<h1 class="mt-1 text-2xl font-semibold tracking-tight text-[var(--text-primary)]">Collectivos</h1>
|
||||
<p class="mt-2 text-[14px] leading-relaxed text-[var(--text-muted)]">
|
||||
Manage group policies end to end — from census intake through billing reconciliation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Workspace tabs -->
|
||||
<div class="col-tabs">
|
||||
<button
|
||||
v-for="tab in [
|
||||
{ id: 'groups' as const, label: 'Active Groups', count: 12 },
|
||||
{ id: 'census' as const, label: 'Census & Members', count: null },
|
||||
{ id: 'billing' as const, label: 'Billing Cycles', count: 3 },
|
||||
]"
|
||||
:key="tab.id"
|
||||
type="button"
|
||||
class="col-tab"
|
||||
:class="activeTab === tab.id ? 'col-tab-active' : 'col-tab-inactive'"
|
||||
@click="activeTab = tab.id"
|
||||
>
|
||||
{{ tab.label }}
|
||||
<span v-if="tab.count" class="col-tab-count" :class="activeTab === tab.id ? 'col-tab-count-active' : ''">{{ tab.count }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ═══ ACTIVE GROUPS ═══ -->
|
||||
<template v-if="activeTab === 'groups'">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<UInput icon="i-heroicons-magnifying-glass" placeholder="Search groups..." size="sm" class="w-64" />
|
||||
<UButton color="primary" size="sm" icon="i-heroicons-plus">New Group Policy</UButton>
|
||||
</div>
|
||||
|
||||
<div class="col-card">
|
||||
<table class="col-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Group</th>
|
||||
<th>Carrier</th>
|
||||
<th>Line</th>
|
||||
<th>Members</th>
|
||||
<th>Premium</th>
|
||||
<th>Renewal</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="group in [
|
||||
{ name: 'Constructora Delta S.A.', carrier: 'ASSA', line: 'Health', members: 84, premium: '$42,600', renewal: '2025-09-01', status: 'Active' },
|
||||
{ name: 'Hotel Pacífico Group', carrier: 'INS', line: 'Life', members: 127, premium: '$78,200', renewal: '2025-07-15', status: 'Active' },
|
||||
{ name: 'Banco Regional', carrier: 'Mapfre', line: 'Health', members: 312, premium: '$186,400', renewal: '2025-11-01', status: 'Active' },
|
||||
{ name: 'Farmacia Salud', carrier: 'ASSA', line: 'Life', members: 23, premium: '$12,800', renewal: '2025-06-01', status: 'Renewal due' },
|
||||
{ name: 'Transportes del Sur', carrier: 'Qualitas', line: 'Auto', members: 56, premium: '$34,100', renewal: '2025-08-15', status: 'Active' },
|
||||
]" :key="group.name" class="col-table-row">
|
||||
<td>
|
||||
<p class="text-[13px] font-medium text-[var(--text-primary)]">{{ group.name }}</p>
|
||||
</td>
|
||||
<td class="text-[13px] text-[var(--text-muted)]">{{ group.carrier }}</td>
|
||||
<td>
|
||||
<span class="col-line-badge">{{ group.line }}</span>
|
||||
</td>
|
||||
<td class="text-[13px] tabular-nums text-[var(--text-primary)]">{{ group.members }}</td>
|
||||
<td class="text-[13px] font-medium tabular-nums text-[var(--text-primary)]">{{ group.premium }}</td>
|
||||
<td class="text-[13px] tabular-nums text-[var(--text-muted)]">{{ group.renewal }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="col-status"
|
||||
:class="group.status === 'Active' ? 'col-status-active' : 'col-status-attention'"
|
||||
>{{ group.status }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- ═══ CENSUS & MEMBERS ═══ -->
|
||||
<template v-if="activeTab === 'census'">
|
||||
<div class="grid gap-4 sm:grid-cols-3">
|
||||
<div class="col-action-card" v-for="action in [
|
||||
{ icon: 'i-heroicons-arrow-up-tray', title: 'Upload Census', desc: 'Import a member roster from Excel or CSV. AI validates and flags issues.' },
|
||||
{ icon: 'i-heroicons-user-plus', title: 'Add Members', desc: 'Add individual members to an active group, with effective date and tier.' },
|
||||
{ icon: 'i-heroicons-arrow-path', title: 'Reconcile Changes', desc: 'Process additions, removals, and tier changes against the carrier file.' },
|
||||
]" :key="action.title">
|
||||
<div class="col-action-icon">
|
||||
<UIcon :name="action.icon" style="width: 20px; height: 20px;" />
|
||||
</div>
|
||||
<p class="mt-3 text-[14px] font-medium text-[var(--text-primary)]">{{ action.title }}</p>
|
||||
<p class="mt-1 text-[12px] leading-relaxed text-[var(--text-muted)]">{{ action.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-card">
|
||||
<div class="col-card-header">
|
||||
<p class="text-[14px] font-semibold text-[var(--text-primary)]">Recent census activity</p>
|
||||
</div>
|
||||
<div class="col-activity-list">
|
||||
<div v-for="event in [
|
||||
{ group: 'Constructora Delta S.A.', action: 'Census uploaded', detail: '84 members validated, 2 flagged', time: '2 hours ago' },
|
||||
{ group: 'Hotel Pacífico Group', action: '3 members added', detail: 'Effective 2025-05-01, Health tier Family', time: 'Yesterday' },
|
||||
{ group: 'Banco Regional', action: 'Reconciliation complete', detail: '+5 additions, -2 removals sent to Mapfre', time: '3 days ago' },
|
||||
{ group: 'Farmacia Salud', action: 'Census flagged', detail: '1 duplicate ID, 1 missing DOB — pending review', time: '5 days ago' },
|
||||
]" :key="event.group + event.action" class="col-activity-row">
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-[13px] text-[var(--text-primary)]">
|
||||
<span class="font-medium">{{ event.group }}</span>
|
||||
<span class="text-[var(--text-muted)]"> — {{ event.action }}</span>
|
||||
</p>
|
||||
<p class="mt-0.5 text-[12px] text-[var(--text-muted)]">{{ event.detail }}</p>
|
||||
</div>
|
||||
<span class="shrink-0 text-[11px] text-[var(--text-muted)]">{{ event.time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- ═══ BILLING CYCLES ═══ -->
|
||||
<template v-if="activeTab === 'billing'">
|
||||
<div class="grid gap-4 sm:grid-cols-3">
|
||||
<div v-for="stat in [
|
||||
{ label: 'Due this month', value: '3', sub: '$247,200 premium' },
|
||||
{ label: 'Pending carrier confirmation', value: '1', sub: 'Awaiting INS response' },
|
||||
{ label: 'Overdue', value: '0', sub: 'All current' },
|
||||
]" :key="stat.label" class="col-stat-card">
|
||||
<p class="text-[11px] font-semibold uppercase tracking-[0.06em] text-[#8a8a86]">{{ stat.label }}</p>
|
||||
<p class="mt-1.5 text-[22px] font-semibold tabular-nums text-[var(--text-primary)]" style="font-variant-numeric: tabular-nums;">{{ stat.value }}</p>
|
||||
<p class="mt-0.5 text-[12px] text-[var(--text-muted)]">{{ stat.sub }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-card">
|
||||
<div class="col-card-header">
|
||||
<p class="text-[14px] font-semibold text-[var(--text-primary)]">Upcoming billing</p>
|
||||
<p class="text-[13px] text-[var(--text-muted)]">Next 60 days</p>
|
||||
</div>
|
||||
<div class="col-activity-list">
|
||||
<div v-for="bill in [
|
||||
{ group: 'Farmacia Salud', period: 'Jun 2025', amount: '$12,800', due: '2025-06-01', status: 'Due soon' },
|
||||
{ group: 'Hotel Pacífico Group', period: 'Jul 2025', amount: '$78,200', due: '2025-07-15', status: 'Scheduled' },
|
||||
{ group: 'Transportes del Sur', period: 'Aug 2025', amount: '$34,100', due: '2025-08-15', status: 'Scheduled' },
|
||||
]" :key="bill.group" class="col-activity-row">
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-[13px] font-medium text-[var(--text-primary)]">{{ bill.group }}</p>
|
||||
<p class="mt-0.5 text-[12px] text-[var(--text-muted)]">{{ bill.period }} · Due {{ bill.due }}</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-[13px] font-medium tabular-nums text-[var(--text-primary)]">{{ bill.amount }}</span>
|
||||
<span
|
||||
class="col-status"
|
||||
:class="bill.status === 'Due soon' ? 'col-status-attention' : 'col-status-neutral'"
|
||||
>{{ bill.status }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* ── Tabs ── */
|
||||
.col-tabs {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
padding: 3px;
|
||||
border-radius: 10px;
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
width: fit-content;
|
||||
}
|
||||
.col-tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 150ms ease;
|
||||
}
|
||||
.col-tab-active {
|
||||
background: #ffffff;
|
||||
color: var(--text-primary);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.col-tab-inactive {
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.col-tab-inactive:hover { color: var(--text-primary); }
|
||||
.col-tab-count {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
padding: 1px 6px;
|
||||
border-radius: 9999px;
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.col-tab-count-active {
|
||||
background: rgba(1, 105, 111, 0.1);
|
||||
color: #01696f;
|
||||
}
|
||||
|
||||
/* ── Card ── */
|
||||
.col-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;
|
||||
}
|
||||
.col-card-header {
|
||||
padding: 20px 20px 16px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* ── Table ── */
|
||||
.col-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
}
|
||||
.col-table th {
|
||||
text-align: left;
|
||||
padding: 10px 16px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: #8a8a86;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.col-table-row td {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.04);
|
||||
vertical-align: middle;
|
||||
}
|
||||
.col-table-row:last-child td { border-bottom: none; }
|
||||
.col-table-row:hover { background: rgba(0, 0, 0, 0.015); }
|
||||
|
||||
/* ── Badges ── */
|
||||
.col-line-badge {
|
||||
display: inline-flex;
|
||||
padding: 2px 8px;
|
||||
border-radius: 6px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
background: rgba(1, 105, 111, 0.06);
|
||||
color: #01696f;
|
||||
}
|
||||
.col-status {
|
||||
display: inline-flex;
|
||||
padding: 2px 8px;
|
||||
border-radius: 9999px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.col-status-active {
|
||||
background: rgba(15, 123, 95, 0.08);
|
||||
color: #0f7b5f;
|
||||
}
|
||||
.col-status-attention {
|
||||
background: rgba(150, 66, 25, 0.08);
|
||||
color: #964219;
|
||||
}
|
||||
.col-status-neutral {
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ── Action cards ── */
|
||||
.col-action-card {
|
||||
padding: 20px;
|
||||
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);
|
||||
cursor: pointer;
|
||||
transition: all 150ms ease;
|
||||
}
|
||||
.col-action-card:hover {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
border-color: rgba(1, 105, 111, 0.2);
|
||||
}
|
||||
.col-action-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
background: rgba(1, 105, 111, 0.06);
|
||||
color: #01696f;
|
||||
}
|
||||
|
||||
/* ── Stat cards ── */
|
||||
.col-stat-card {
|
||||
padding: 20px;
|
||||
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);
|
||||
}
|
||||
|
||||
/* ── Activity list ── */
|
||||
.col-activity-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.col-activity-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 14px 20px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.col-activity-row:last-child { border-bottom: none; }
|
||||
.col-activity-row:hover { background: rgba(0, 0, 0, 0.015); }
|
||||
</style>
|
||||
Reference in New Issue
Block a user