feat: routines module; super admin subscription badge, 7-day trial and lifetime plan
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -38,6 +38,7 @@ const hubStack = ref<NavHubKey[]>([]);
|
||||
const isMobileNav = ref(false);
|
||||
const globalSearch = ref('');
|
||||
const activityBadge = ref(0);
|
||||
const routineBadge = ref(0);
|
||||
|
||||
function pathIn(prefixes: string[]) {
|
||||
return prefixes.some(
|
||||
@@ -67,6 +68,7 @@ const salesActive = computed(() =>
|
||||
);
|
||||
const activitiesActive = computed(() => pathIn(['/activities']));
|
||||
const notesActive = computed(() => route.path === '/notes' || route.path.startsWith('/notes/'));
|
||||
const routinesActive = computed(() => pathIn(['/routines']));
|
||||
const conversationsActive = computed(() => pathIn(['/conversations']));
|
||||
const supportActive = computed(() => pathIn(['/tickets']));
|
||||
const automationsActive = computed(
|
||||
@@ -100,6 +102,7 @@ const settingsActive = computed(() =>
|
||||
);
|
||||
const moreSectionActive = computed(
|
||||
() =>
|
||||
routinesActive.value ||
|
||||
notesActive.value ||
|
||||
conversationsActive.value ||
|
||||
supportActive.value ||
|
||||
@@ -285,6 +288,16 @@ async function loadActivityBadge() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRoutineBadge() {
|
||||
if (!moduleAllowed('routines')) return;
|
||||
try {
|
||||
const { data } = await api.get<{ remaining: number; overdue: number }>('/routine-tasks/badge');
|
||||
routineBadge.value = data.remaining + data.overdue;
|
||||
} catch {
|
||||
routineBadge.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadChatBadge() {
|
||||
if (!moduleAllowed('conversations')) return;
|
||||
try {
|
||||
@@ -374,9 +387,11 @@ onMounted(() => {
|
||||
}
|
||||
void refreshNotifs();
|
||||
void loadActivityBadge();
|
||||
void loadRoutineBadge();
|
||||
void loadChatBadge();
|
||||
pollTimer = setInterval(() => {
|
||||
void refreshNotifs();
|
||||
void loadRoutineBadge();
|
||||
void refreshUnreadTotal().catch(() => undefined);
|
||||
}, 45000);
|
||||
mobileMq = window.matchMedia('(max-width: 900px)');
|
||||
@@ -510,6 +525,19 @@ onUnmounted(() => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<RouterLink
|
||||
v-if="moduleAllowed('routines')"
|
||||
to="/routines"
|
||||
class="nav-item"
|
||||
title="کارهای روتین"
|
||||
:class="{ 'router-link-active': routinesActive }"
|
||||
@click="closeHub(); closeMenu()"
|
||||
>
|
||||
<UiIcon name="refresh" :size="collapsed ? 20 : 18" />
|
||||
<span>کارهای روتین</span>
|
||||
<span v-if="routineBadge" class="nav-badge">{{ toFaDigits(routineBadge) }}</span>
|
||||
</RouterLink>
|
||||
|
||||
<RouterLink
|
||||
v-if="moduleAllowed('conversations')"
|
||||
to="/conversations"
|
||||
@@ -725,7 +753,8 @@ onUnmounted(() => {
|
||||
:title="`انقضا: ${subscription.expiresAt}`"
|
||||
>
|
||||
<UiIcon name="clock" :size="15" />
|
||||
<span>
|
||||
<span v-if="subscription.planCode === 'lifetime'">اشتراک دائمی</span>
|
||||
<span v-else>
|
||||
{{ toFaDigits(subscription.daysRemaining) }} روز باقیمانده
|
||||
</span>
|
||||
</div>
|
||||
@@ -858,6 +887,17 @@ onUnmounted(() => {
|
||||
<button class="round-btn sm" type="button" aria-label="بستن" @click="closeMore">✕</button>
|
||||
</header>
|
||||
<nav class="more-body">
|
||||
<RouterLink
|
||||
v-if="moduleAllowed('routines')"
|
||||
to="/routines"
|
||||
class="nav-item"
|
||||
:class="{ 'router-link-active': routinesActive }"
|
||||
@click="closeHub(); closeMore()"
|
||||
>
|
||||
<UiIcon name="refresh" :size="18" />
|
||||
<span>کارهای روتین</span>
|
||||
<span v-if="routineBadge" class="nav-badge">{{ toFaDigits(routineBadge) }}</span>
|
||||
</RouterLink>
|
||||
<RouterLink to="/notes" class="nav-item" @click="closeHub(); closeMore()">
|
||||
<UiIcon name="document" :size="18" />
|
||||
<span>یادداشتها</span>
|
||||
|
||||
Reference in New Issue
Block a user