feat: add DISC profiles and a sales copilot as an org module
Help reps capture a customer's likely behavior style and get negotiation guidance, with the feature gated from super admin. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { RouterLink } from 'vue-router';
|
||||
import type { DiscDistributionRow } from '@mento/shared';
|
||||
import { api } from '@/lib/api';
|
||||
import { toFaDigits } from '@/lib/jalali';
|
||||
import { UiButton, UiPageHeader } from '@/components/ui';
|
||||
|
||||
const rows = ref<DiscDistributionRow[]>([]);
|
||||
const error = ref('');
|
||||
const loading = ref(true);
|
||||
const exporting = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const { data } = await api.get<DiscDistributionRow[]>('/behavior/disc/distribution');
|
||||
rows.value = data;
|
||||
} catch {
|
||||
error.value = 'گزارش توزیع تیپها بارگذاری نشد.';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
async function exportExcel() {
|
||||
exporting.value = true;
|
||||
error.value = '';
|
||||
try {
|
||||
const { data } = await api.get<Blob>('/behavior/disc/export', { responseType: 'blob' });
|
||||
const url = URL.createObjectURL(data);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'disc.xlsx';
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
} catch {
|
||||
error.value = 'خروجی اکسل ناموفق بود.';
|
||||
} finally {
|
||||
exporting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const total = () => rows.value.reduce((sum, row) => sum + row.count, 0);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="page">
|
||||
<UiPageHeader title="توزیع تیپهای DISC" lead="فقط ارزیابیهای تأییدشده و قابل اتکا، در محدودهٔ دسترسی شما" :back-to="{ name: 'contacts' }">
|
||||
<template #actions>
|
||||
<UiButton variant="outline" :disabled="exporting" @click="exportExcel">خروجی اکسل</UiButton>
|
||||
</template>
|
||||
</UiPageHeader>
|
||||
<p v-if="loading" class="muted">در حال بارگذاری…</p>
|
||||
<p v-else-if="error" class="error">{{ error }}</p>
|
||||
<p v-else-if="!rows.length" class="muted">هنوز تیپ قابل اتکایی ثبت نشده است.</p>
|
||||
<ul v-else class="list">
|
||||
<li v-for="row in rows" :key="row.label">
|
||||
<RouterLink :to="{ name: 'contacts', query: { disc: row.label } }">{{ row.label }}</RouterLink>
|
||||
<span>{{ toFaDigits(row.count) }}</span>
|
||||
<i><b :style="{ width: `${total() ? Math.round((row.count / total()) * 100) : 0}%` }" /></i>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="muted">درصدها سهم مشتریان دارای ارزیابی معتبر هستند، نه دقت یک آزمون روانشناسی.</p>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page { display: flex; flex-direction: column; gap: 0.8rem; }
|
||||
.list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.55rem; }
|
||||
.list li { display: grid; grid-template-columns: 6rem auto 1fr; gap: 0.6rem; align-items: center; }
|
||||
.list i { height: 8px; background: #e2e8f0; border-radius: 99px; overflow: hidden; }
|
||||
.list b { display: block; height: 100%; background: #0f766e; }
|
||||
.muted { color: #64748b; }
|
||||
.error { color: #b91c1c; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user