fix(crm): mesghal by metal, jalali fit, scoped group filters

This commit is contained in:
2026-10-24 22:25:27 +03:30
parent f8128ebe3a
commit 48815852fc
8 changed files with 202 additions and 27 deletions
+28 -13
View File
@@ -24,6 +24,7 @@ import SavedFiltersBar from '@/components/SavedFiltersBar.vue';
import TableColumnsMenu from '@/components/TableColumnsMenu.vue';
import type {
CompanyDto,
CustomerGroupDto,
ImportContactsResult,
PaginatedResponse,
PersonDto,
@@ -82,6 +83,8 @@ const discFilter = computed(() => {
const sourceFilters = computed(() => queryList('source'));
const groupFilters = computed(() => queryList('group'));
const { groups: customerGroups, load: loadCustomerGroups } = useCustomerGroups();
/** گروه‌های فیلتر — با شمارش scoped بر اساس دسترسی کاربر */
const filterGroups = ref<CustomerGroupDto[]>([]);
const canManageGroups = computed(() => canManageSettings(auth.user?.role));
const groupsManagerOpen = ref(false);
@@ -95,7 +98,9 @@ const filterValue = computed<ContactsFilterValue>(() => ({
type FilterTag = { kind: 'group' | 'source' | 'mood' | 'disc'; value: string; label: string; color?: string };
const activeTags = computed<FilterTag[]>(() => {
const tags: FilterTag[] = groupFilters.value.map((id) => {
const g = customerGroups.value.find((x) => x.id === id);
const g =
filterGroups.value.find((x) => x.id === id) ??
customerGroups.value.find((x) => x.id === id);
return { kind: 'group', value: id, label: g?.name ?? 'گروه حذف‌شده', color: g?.color };
});
if (tab.value !== 'people') return tags;
@@ -303,7 +308,7 @@ async function load() {
void loadCustomerGroups(true).catch(() => undefined);
try {
if (tab.value === 'people') {
const [peopleRes, statsRes] = await Promise.all([
const [peopleRes, statsRes, groupStatsRes] = await Promise.all([
api.get<PaginatedResponse<PersonDto>>('/people', {
params: {
q: q.value || undefined,
@@ -319,21 +324,31 @@ async function load() {
api.get<PersonImportSourceStatDto[]>('/people/source-stats').catch(() => ({
data: [] as PersonImportSourceStatDto[],
})),
api.get<CustomerGroupDto[]>('/people/group-stats').catch(() => ({
data: [] as CustomerGroupDto[],
})),
]);
people.value = peopleRes.data;
sourceStats.value = statsRes.data;
filterGroups.value = groupStatsRes.data;
const visible = new Set(peopleRes.data.items.map((p) => p.id));
selectedIds.value = selectedIds.value.filter((id) => visible.has(id));
} else {
const { data } = await api.get<PaginatedResponse<CompanyDto>>('/companies', {
params: {
q: q.value || undefined,
page: page.value,
limit: pageSize.value,
groupId: csv(groupFilters.value),
},
});
companies.value = data;
const [companiesRes, groupStatsRes] = await Promise.all([
api.get<PaginatedResponse<CompanyDto>>('/companies', {
params: {
q: q.value || undefined,
page: page.value,
limit: pageSize.value,
groupId: csv(groupFilters.value),
},
}),
api.get<CustomerGroupDto[]>('/companies/group-stats').catch(() => ({
data: [] as CustomerGroupDto[],
})),
]);
companies.value = companiesRes.data;
filterGroups.value = groupStatsRes.data;
}
} catch (e) {
if (axios.isAxiosError(e) && !e.response) {
@@ -704,7 +719,7 @@ onMounted(load);
:open="filterOpen"
:tab="tab"
:value="filterValue"
:groups="customerGroups"
:groups="filterGroups"
:source-stats="sourceStats"
:can-behavior="canBehavior"
:sort="sort"
@@ -739,7 +754,7 @@ onMounted(load);
:open="filterOpen"
:tab="tab"
:value="filterValue"
:groups="customerGroups"
:groups="filterGroups"
:source-stats="sourceStats"
:can-behavior="canBehavior"
@apply="applyFilters"