Match Leads stats cards to the mockup and fix Contacts double menus.
Stage summary uses the green total card plus paired/solo white cells; people row menus use separate mobile/desktop keys.
This commit is contained in:
@@ -406,9 +406,9 @@ onMounted(load);
|
||||
{{ p.fullName }}
|
||||
</RouterLink>
|
||||
<UiDropdownMenu
|
||||
:open="menuId === p.id"
|
||||
:open="menuId === `m:${p.id}`"
|
||||
align="end"
|
||||
@update:open="(v) => (menuId = v ? p.id : null)"
|
||||
@update:open="(v) => (menuId = v ? `m:${p.id}` : null)"
|
||||
>
|
||||
<template #trigger="{ toggle }">
|
||||
<button class="more" type="button" @click.stop="toggle">
|
||||
@@ -491,9 +491,9 @@ onMounted(load);
|
||||
<td>{{ formatDate(p.createdAt) }}</td>
|
||||
<td class="ops" @click.stop>
|
||||
<UiDropdownMenu
|
||||
:open="menuId === p.id"
|
||||
:open="menuId === `t:${p.id}`"
|
||||
align="end"
|
||||
@update:open="(v) => (menuId = v ? p.id : null)"
|
||||
@update:open="(v) => (menuId = v ? `t:${p.id}` : null)"
|
||||
>
|
||||
<template #trigger="{ toggle }">
|
||||
<button type="button" class="more" @click.stop="toggle">
|
||||
|
||||
+185
-106
@@ -148,6 +148,13 @@ const stageCards = computed(() => {
|
||||
return board.slice(0, 5);
|
||||
});
|
||||
|
||||
const pairStages = computed(() =>
|
||||
stageCards.value.length >= 2 ? stageCards.value.slice(0, 2) : [],
|
||||
);
|
||||
const soloStages = computed(() =>
|
||||
stageCards.value.length >= 2 ? stageCards.value.slice(2) : stageCards.value,
|
||||
);
|
||||
|
||||
const pageNumbers = computed(() => {
|
||||
const totalPages = pageCount.value;
|
||||
const current = page.value;
|
||||
@@ -809,31 +816,47 @@ onBeforeUnmount(stopImportPoll);
|
||||
<div class="stats" role="list">
|
||||
<button
|
||||
type="button"
|
||||
class="stat mint"
|
||||
class="stat-total"
|
||||
:class="{ on: !stageId }"
|
||||
role="listitem"
|
||||
@click="setStageFilter('')"
|
||||
>
|
||||
<span class="stat-ico"><UiIcon name="users" :size="18" /></span>
|
||||
<span class="stat-body">
|
||||
<span class="stat-total-ico" aria-hidden="true">
|
||||
<UiIcon name="users" :size="18" />
|
||||
</span>
|
||||
<span class="stat-total-body">
|
||||
<span class="stat-label">همه لیدها</span>
|
||||
<strong class="stat-num">{{ formatCount(stats?.total ?? total) }}</strong>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div v-if="pairStages.length" class="stat-pair" role="listitem">
|
||||
<button
|
||||
v-for="s in pairStages"
|
||||
:key="s.id"
|
||||
type="button"
|
||||
class="stat-cell"
|
||||
:class="{ on: stageId === s.id }"
|
||||
@click="setStageFilter(s.id)"
|
||||
>
|
||||
<i class="stat-dot" :data-tint="stageTint(s.color)" aria-hidden="true" />
|
||||
<span class="stat-label">{{ s.name }}</span>
|
||||
<strong class="stat-num">{{ formatCount(s.leadCount ?? 0) }}</strong>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
v-for="s in stageCards"
|
||||
v-for="s in soloStages"
|
||||
:key="s.id"
|
||||
type="button"
|
||||
class="stat"
|
||||
:class="[stageTint(s.color), { on: stageId === s.id }]"
|
||||
class="stat-cell alone"
|
||||
:class="{ on: stageId === s.id }"
|
||||
role="listitem"
|
||||
@click="setStageFilter(s.id)"
|
||||
>
|
||||
<span class="stat-ico"><UiIcon name="target" :size="18" /></span>
|
||||
<span class="stat-body">
|
||||
<span class="stat-label">{{ s.name }}</span>
|
||||
<strong class="stat-num">{{ formatCount(s.leadCount ?? 0) }}</strong>
|
||||
</span>
|
||||
<i class="stat-dot" :data-tint="stageTint(s.color)" aria-hidden="true" />
|
||||
<span class="stat-label">{{ s.name }}</span>
|
||||
<strong class="stat-num">{{ formatCount(s.leadCount ?? 0) }}</strong>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1289,135 +1312,191 @@ onBeforeUnmount(stopImportPoll);
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
|
||||
gap: 0.65rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: stretch;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.stat {
|
||||
.stat-total {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.7rem;
|
||||
text-align: start;
|
||||
padding: 0.85rem 0.95rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid transparent;
|
||||
background: #fff;
|
||||
gap: 0.75rem;
|
||||
min-width: 9.5rem;
|
||||
padding: 0.85rem 1rem;
|
||||
border: 1px solid #bbf7d0;
|
||||
border-radius: 14px;
|
||||
background: #ecfdf5;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
color: var(--ink);
|
||||
text-align: start;
|
||||
color: #047857;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||
transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
|
||||
transition: box-shadow 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
.stat:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 16px rgba(15, 23, 42, 0.08);
|
||||
.stat-total:hover,
|
||||
.stat-total.on {
|
||||
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.22);
|
||||
border-color: #86efac;
|
||||
}
|
||||
|
||||
.stat.on {
|
||||
border-color: color-mix(in srgb, var(--accent) 45%, transparent);
|
||||
box-shadow: 0 0 0 2px var(--accent-soft);
|
||||
}
|
||||
|
||||
.stat-ico {
|
||||
width: 2.35rem;
|
||||
height: 2.35rem;
|
||||
.stat-total-ico {
|
||||
width: 2.4rem;
|
||||
height: 2.4rem;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
color: #10b981;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
.stat-body {
|
||||
.stat-total-body {
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
gap: 0.1rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.78rem;
|
||||
color: var(--muted);
|
||||
.stat-total .stat-label {
|
||||
color: #059669;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stat-total .stat-num {
|
||||
color: #047857;
|
||||
font-size: 1.35rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.stat-pair {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex: 1 1 14rem;
|
||||
min-width: 12rem;
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 14px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
.stat-pair .stat-cell {
|
||||
flex: 1 1 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.stat-pair .stat-cell + .stat-cell {
|
||||
border-inline-start: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.stat-cell {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.2rem;
|
||||
min-width: 6.5rem;
|
||||
padding: 0.85rem 0.9rem 0.75rem;
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 14px;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
color: #0f172a;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||
transition: box-shadow 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
.stat-cell.alone {
|
||||
flex: 0 1 7.5rem;
|
||||
}
|
||||
|
||||
.stat-cell:hover,
|
||||
.stat-cell.on {
|
||||
border-color: color-mix(in srgb, var(--accent) 40%, #e5e7eb);
|
||||
box-shadow: 0 0 0 2px var(--accent-soft);
|
||||
}
|
||||
|
||||
.stat-pair:has(.stat-cell.on) {
|
||||
border-color: color-mix(in srgb, var(--accent) 40%, #e5e7eb);
|
||||
box-shadow: 0 0 0 2px var(--accent-soft);
|
||||
}
|
||||
|
||||
.stat-dot {
|
||||
width: 0.55rem;
|
||||
height: 0.55rem;
|
||||
border-radius: 999px;
|
||||
margin-bottom: 0.15rem;
|
||||
background: #94a3b8;
|
||||
box-shadow: 0 0 0 4px color-mix(in srgb, #94a3b8 18%, transparent);
|
||||
}
|
||||
|
||||
.stat-dot[data-tint='mint'],
|
||||
.stat-dot[data-tint='teal'] {
|
||||
background: #10b981;
|
||||
box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.18);
|
||||
}
|
||||
.stat-dot[data-tint='amber'] {
|
||||
background: #f59e0b;
|
||||
box-shadow: 0 0 0 4px rgba(245, 158, 11, 0.18);
|
||||
}
|
||||
.stat-dot[data-tint='peach'] {
|
||||
background: #f97316;
|
||||
box-shadow: 0 0 0 4px rgba(249, 115, 22, 0.18);
|
||||
}
|
||||
.stat-dot[data-tint='sky'] {
|
||||
background: #0ea5e9;
|
||||
box-shadow: 0 0 0 4px rgba(14, 165, 233, 0.18);
|
||||
}
|
||||
.stat-dot[data-tint='violet'] {
|
||||
background: #8b5cf6;
|
||||
box-shadow: 0 0 0 4px rgba(139, 92, 246, 0.18);
|
||||
}
|
||||
.stat-dot[data-tint='rose'] {
|
||||
background: #ef4444;
|
||||
box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.18);
|
||||
}
|
||||
|
||||
.stat-cell .stat-label {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
color: #64748b;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.stat-num {
|
||||
.stat-cell .stat-num {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 800;
|
||||
color: #0f172a;
|
||||
letter-spacing: -0.02em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.stat.mint {
|
||||
background: linear-gradient(180deg, #f0fdf6 0%, #fff 100%);
|
||||
}
|
||||
.stat.mint .stat-ico {
|
||||
background: #d1fae5;
|
||||
color: #059669;
|
||||
}
|
||||
.stat.mint .stat-num {
|
||||
color: #047857;
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.stat-pair {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
.stat.amber {
|
||||
background: linear-gradient(180deg, #fffbeb 0%, #fff 100%);
|
||||
}
|
||||
.stat.amber .stat-ico {
|
||||
background: #fef3c7;
|
||||
color: #d97706;
|
||||
}
|
||||
.stat.amber .stat-num {
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.stat.peach {
|
||||
background: linear-gradient(180deg, #fff7ed 0%, #fff 100%);
|
||||
}
|
||||
.stat.peach .stat-ico {
|
||||
background: #ffedd5;
|
||||
color: #ea580c;
|
||||
}
|
||||
.stat.peach .stat-num {
|
||||
color: #c2410c;
|
||||
}
|
||||
|
||||
.stat.sky,
|
||||
.stat.teal {
|
||||
background: linear-gradient(180deg, #ecfdf5 0%, #fff 100%);
|
||||
}
|
||||
.stat.sky .stat-ico,
|
||||
.stat.teal .stat-ico {
|
||||
background: #ccfbf1;
|
||||
color: #0d9488;
|
||||
}
|
||||
.stat.sky .stat-num,
|
||||
.stat.teal .stat-num {
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.stat.violet {
|
||||
background: linear-gradient(180deg, #f5f3ff 0%, #fff 100%);
|
||||
}
|
||||
.stat.violet .stat-ico {
|
||||
background: #ede9fe;
|
||||
color: #7c3aed;
|
||||
}
|
||||
.stat.violet .stat-num {
|
||||
color: #6d28d9;
|
||||
}
|
||||
|
||||
.stat.rose {
|
||||
background: linear-gradient(180deg, #fef2f2 0%, #fff 100%);
|
||||
}
|
||||
.stat.rose .stat-ico {
|
||||
background: #fee2e2;
|
||||
color: #dc2626;
|
||||
}
|
||||
.stat.rose .stat-num {
|
||||
color: #b91c1c;
|
||||
.stat-cell.alone,
|
||||
.stat-total {
|
||||
flex: 1 1 calc(50% - 0.4rem);
|
||||
}
|
||||
}
|
||||
|
||||
.filters {
|
||||
|
||||
Reference in New Issue
Block a user