Improve Leads mobile UX and nest leads under Customers nav.
Restore the 5-tab mobile bar, add responsive lead cards with long-press select, and fix stats/filters layout.
This commit is contained in:
@@ -32,6 +32,8 @@ const SIDEBAR_KEY = 'mento_sidebar_collapsed';
|
||||
const collapsed = ref(false);
|
||||
|
||||
const openHubKey = ref<NavHubKey | null>(null);
|
||||
const hubStack = ref<NavHubKey[]>([]);
|
||||
const isMobileNav = ref(false);
|
||||
const globalSearch = ref('');
|
||||
const activityBadge = ref(0);
|
||||
|
||||
@@ -44,10 +46,12 @@ function pathIn(prefixes: string[]) {
|
||||
const dashboardActive = computed(() => route.path === '/');
|
||||
const loyaltyActive = computed(() => pathIn(['/loyalty']));
|
||||
const reportsActive = computed(() => pathIn(['/reports']));
|
||||
const customersActive = computed(() => pathIn(['/contacts']));
|
||||
const leadsActive = computed(() =>
|
||||
pathIn(['/leads', '/settings/lead-stages', '/settings/lead-forms']),
|
||||
);
|
||||
const customersActive = computed(
|
||||
() => pathIn(['/contacts']) || (isMobileNav.value && leadsActive.value),
|
||||
);
|
||||
const salesActive = computed(() =>
|
||||
pathIn([
|
||||
'/pipeline',
|
||||
@@ -111,23 +115,6 @@ function moduleAllowed(module?: string) {
|
||||
return enabledModules.value.includes(module as never);
|
||||
}
|
||||
|
||||
const openHub = computed<NavHubDef | null>(() => {
|
||||
if (!openHubKey.value) return null;
|
||||
const hub = NAV_HUBS.find((item) => item.key === openHubKey.value);
|
||||
if (!hub) return null;
|
||||
return {
|
||||
...hub,
|
||||
cards: hub.cards.filter(
|
||||
(card) =>
|
||||
(!card.admin || canSettings.value) && moduleAllowed(card.module),
|
||||
),
|
||||
shortcuts: hub.shortcuts.filter(
|
||||
(item) =>
|
||||
(!item.admin || canSettings.value) && moduleAllowed(item.module),
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
const hubVisible = computed(() => {
|
||||
const map: Record<string, boolean> = {};
|
||||
for (const hub of NAV_HUBS) {
|
||||
@@ -139,6 +126,41 @@ const hubVisible = computed(() => {
|
||||
return map;
|
||||
});
|
||||
|
||||
const openHub = computed<NavHubDef | null>(() => {
|
||||
if (!openHubKey.value) return null;
|
||||
const hub = NAV_HUBS.find((item) => item.key === openHubKey.value);
|
||||
if (!hub) return null;
|
||||
const cards = hub.cards.filter(
|
||||
(card) =>
|
||||
(!card.admin || canSettings.value) && moduleAllowed(card.module),
|
||||
);
|
||||
if (
|
||||
hub.key === 'customers' &&
|
||||
isMobileNav.value &&
|
||||
hubVisible.value.leads
|
||||
) {
|
||||
cards.push({
|
||||
to: '/leads',
|
||||
hub: 'leads',
|
||||
title: 'لیدها',
|
||||
desc: 'مخاطبین بالقوه قبل از تبدیل به مشتری',
|
||||
icon: 'user-plus',
|
||||
tint: 'sky',
|
||||
module: 'leads',
|
||||
});
|
||||
}
|
||||
return {
|
||||
...hub,
|
||||
cards,
|
||||
shortcuts: hub.shortcuts.filter(
|
||||
(item) =>
|
||||
(!item.admin || canSettings.value) && moduleAllowed(item.module),
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
const hubCanBack = computed(() => hubStack.value.length > 0);
|
||||
|
||||
function givenName(fullName: string): string {
|
||||
const parts = fullName.trim().split(/\s+/).filter(Boolean);
|
||||
if (parts.length <= 1) return fullName.trim();
|
||||
@@ -177,12 +199,31 @@ watch([profileFullName, collapsed], () => {
|
||||
|
||||
function closeHub() {
|
||||
openHubKey.value = null;
|
||||
hubStack.value = [];
|
||||
}
|
||||
|
||||
function onHubClick(key: NavHubKey) {
|
||||
hubStack.value = [];
|
||||
openHubKey.value = openHubKey.value === key ? null : key;
|
||||
}
|
||||
|
||||
function openNestedHub(key: NavHubKey) {
|
||||
if (openHubKey.value) {
|
||||
hubStack.value = [...hubStack.value, openHubKey.value];
|
||||
}
|
||||
openHubKey.value = key;
|
||||
}
|
||||
|
||||
function backHub() {
|
||||
const prev = hubStack.value[hubStack.value.length - 1];
|
||||
if (!prev) {
|
||||
closeHub();
|
||||
return;
|
||||
}
|
||||
hubStack.value = hubStack.value.slice(0, -1);
|
||||
openHubKey.value = prev;
|
||||
}
|
||||
|
||||
function openTabHub(key: NavHubKey) {
|
||||
moreOpen.value = false;
|
||||
notifOpen.value = false;
|
||||
@@ -233,7 +274,11 @@ function closeNotif() {
|
||||
}
|
||||
|
||||
function onMobileMqChange(event: MediaQueryListEvent) {
|
||||
if (!event.matches) closeMore();
|
||||
isMobileNav.value = event.matches;
|
||||
if (!event.matches) {
|
||||
closeMore();
|
||||
hubStack.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
function onLogout() {
|
||||
@@ -358,6 +403,7 @@ onMounted(() => {
|
||||
if (meta) profileNameObserver.observe(meta);
|
||||
}
|
||||
mobileMq = window.matchMedia('(max-width: 900px)');
|
||||
isMobileNav.value = mobileMq.matches;
|
||||
mobileMq.addEventListener('change', onMobileMqChange);
|
||||
});
|
||||
|
||||
@@ -717,7 +763,14 @@ onUnmounted(() => {
|
||||
|
||||
<div v-if="openHub" class="hub-layer">
|
||||
<button class="hub-dim" type="button" aria-label="بستن منو" @click="closeHub" />
|
||||
<NavHubPanel :hub="openHub" @close="closeHub" @go="goHub" />
|
||||
<NavHubPanel
|
||||
:hub="openHub"
|
||||
:can-back="hubCanBack"
|
||||
@close="closeHub"
|
||||
@back="backHub"
|
||||
@go="goHub"
|
||||
@open-hub="openNestedHub"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<nav class="mobile-tabbar mobile-only" aria-label="منوی اصلی">
|
||||
@@ -733,23 +786,21 @@ onUnmounted(() => {
|
||||
<button
|
||||
class="tab"
|
||||
type="button"
|
||||
:class="{ on: !moreOpen && (customersActive || openHubKey === 'customers') }"
|
||||
:aria-expanded="openHubKey === 'customers'"
|
||||
:class="{
|
||||
on:
|
||||
!moreOpen &&
|
||||
(customersActive ||
|
||||
openHubKey === 'customers' ||
|
||||
(isMobileNav && openHubKey === 'leads')),
|
||||
}"
|
||||
:aria-expanded="
|
||||
openHubKey === 'customers' || (isMobileNav && openHubKey === 'leads')
|
||||
"
|
||||
@click="openTabHub('customers')"
|
||||
>
|
||||
<UiIcon name="users" :size="20" />
|
||||
<span>مشتریان</span>
|
||||
</button>
|
||||
<button
|
||||
class="tab"
|
||||
type="button"
|
||||
:class="{ on: !moreOpen && (leadsActive || openHubKey === 'leads') }"
|
||||
:aria-expanded="openHubKey === 'leads'"
|
||||
@click="openTabHub('leads')"
|
||||
>
|
||||
<UiIcon name="user-plus" :size="20" />
|
||||
<span>لیدها</span>
|
||||
</button>
|
||||
<button
|
||||
class="tab"
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user