Drop groupIds from customer data-access filters; keep visibility via group assignees only.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -115,9 +115,7 @@ export function buildContactRuleWhere(
|
||||
if (!ids.length) return null;
|
||||
and.push(ids.length === 1 ? { ownerId: ids[0]! } : { ownerId: { in: ids } });
|
||||
}
|
||||
if (rule.groupIds?.length) {
|
||||
and.push({ groupLinks: { some: { groupId: { in: rule.groupIds } } } });
|
||||
}
|
||||
// groupIds دیگر فیلتر دسترسی نیست؛ دید گروه فقط از مسئولگروه (personViaGroupAssignee)
|
||||
if (rule.sources?.length) {
|
||||
and.push({
|
||||
OR: [{ importSource: { in: rule.sources } }, { sourceRef: { in: rule.sources } }],
|
||||
@@ -188,20 +186,13 @@ export function customersCompanyWhere(
|
||||
if (!ids.length) continue;
|
||||
and.push(ids.length === 1 ? { ownerId: ids[0]! } : { ownerId: { in: ids } });
|
||||
}
|
||||
if (rule.groupIds?.length) {
|
||||
and.push({ groupLinks: { some: { groupId: { in: rule.groupIds } } } });
|
||||
}
|
||||
// groupIds دیگر فیلتر دسترسی نیست؛ دید گروه فقط از مسئولگروه (companyViaGroupAssignee)
|
||||
if (rule.sources?.length) {
|
||||
and.push({
|
||||
OR: [{ importSource: { in: rule.sources } }, { sourceRef: { in: rule.sources } }],
|
||||
});
|
||||
}
|
||||
if (
|
||||
rule.moods?.length &&
|
||||
!rule.owners?.length &&
|
||||
!rule.groupIds?.length &&
|
||||
!rule.sources?.length
|
||||
) {
|
||||
if (rule.moods?.length && !rule.owners?.length && !rule.sources?.length) {
|
||||
continue;
|
||||
}
|
||||
if (!and.length) continue;
|
||||
@@ -469,9 +460,14 @@ export function sectionOwnerIdUnion(
|
||||
if (rule.owners?.length) {
|
||||
sawOwnerRule = true;
|
||||
for (const id of ownerIdList(rule, ctx)) ids.add(id);
|
||||
} else if (rule.groupIds?.length || rule.sources?.length || rule.moods?.length ||
|
||||
rule.stageIds?.length || rule.pipelineIds?.length || rule.boardIds?.length ||
|
||||
rule.typeIds?.length) {
|
||||
} else if (
|
||||
rule.sources?.length ||
|
||||
rule.moods?.length ||
|
||||
rule.stageIds?.length ||
|
||||
rule.pipelineIds?.length ||
|
||||
rule.boardIds?.length ||
|
||||
rule.typeIds?.length
|
||||
) {
|
||||
sawNonOwnerRule = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: SectionDataAccess;
|
||||
section?: DataAccessSection;
|
||||
groups?: IdOption[];
|
||||
stages?: IdOption[];
|
||||
pipelines?: IdOption[];
|
||||
boards?: IdOption[];
|
||||
@@ -36,7 +35,6 @@ const props = withDefaults(
|
||||
}>(),
|
||||
{
|
||||
section: 'customers',
|
||||
groups: () => [],
|
||||
stages: () => [],
|
||||
pipelines: () => [],
|
||||
boards: () => [],
|
||||
@@ -53,9 +51,9 @@ const emit = defineEmits<{
|
||||
|
||||
const sectionLabel = computed(() => DATA_ACCESS_SECTION_LABELS[props.section]);
|
||||
|
||||
const showGroups = computed(() => props.section === 'customers');
|
||||
const showMoods = computed(() => props.section === 'customers');
|
||||
const showImportSources = computed(() => props.section === 'customers');
|
||||
const isCustomers = computed(() => props.section === 'customers');
|
||||
const showLeadSources = computed(() => props.section === 'leads');
|
||||
const showNoteTypes = computed(() => props.section === 'notes');
|
||||
const showTicketStatuses = computed(() => props.section === 'tickets');
|
||||
@@ -119,9 +117,8 @@ const boardsLegend = computed(() =>
|
||||
/** چند قانون قدیمی → یک فیلتر (اتحاد گزینهها) برای ویرایش ساده */
|
||||
function collapseRules(rules: DataAccessRule[]): DataAccessRule {
|
||||
if (!rules.length) return {};
|
||||
if (rules.length === 1) return { ...rules[0]! };
|
||||
if (rules.length === 1) return stripCustomerGroupIds({ ...rules[0]! });
|
||||
const owners = new Set<DataAccessOwnerKind>();
|
||||
const groupIds = new Set<string>();
|
||||
const sources = new Set<string>();
|
||||
const moods = new Set<PersonMood>();
|
||||
const stageIds = new Set<string>();
|
||||
@@ -130,7 +127,6 @@ function collapseRules(rules: DataAccessRule[]): DataAccessRule {
|
||||
const typeIds = new Set<string>();
|
||||
for (const r of rules) {
|
||||
for (const o of r.owners ?? []) owners.add(o);
|
||||
for (const id of r.groupIds ?? []) groupIds.add(id);
|
||||
for (const s of r.sources ?? []) sources.add(s);
|
||||
for (const m of r.moods ?? []) moods.add(m);
|
||||
for (const id of r.stageIds ?? []) stageIds.add(id);
|
||||
@@ -140,7 +136,6 @@ function collapseRules(rules: DataAccessRule[]): DataAccessRule {
|
||||
}
|
||||
const out: DataAccessRule = {};
|
||||
if (owners.size) out.owners = [...owners];
|
||||
if (groupIds.size) out.groupIds = [...groupIds];
|
||||
if (sources.size) out.sources = [...sources];
|
||||
if (moods.size) out.moods = [...moods];
|
||||
if (stageIds.size) out.stageIds = [...stageIds];
|
||||
@@ -150,10 +145,17 @@ function collapseRules(rules: DataAccessRule[]): DataAccessRule {
|
||||
return out;
|
||||
}
|
||||
|
||||
/** دسترسی گروه فقط از مسئولگروه؛ groupIds دیگر در فیلتر مشتریان ذخیره/نمایش نمیشود */
|
||||
function stripCustomerGroupIds(rule: DataAccessRule): DataAccessRule {
|
||||
if (!isCustomers.value || !rule.groupIds?.length) return rule;
|
||||
const { groupIds: _drop, ...rest } = rule;
|
||||
return rest;
|
||||
}
|
||||
|
||||
function ruleHasAnyFilter(rule: DataAccessRule): boolean {
|
||||
return Boolean(
|
||||
rule.owners?.length ||
|
||||
rule.groupIds?.length ||
|
||||
(!isCustomers.value && rule.groupIds?.length) ||
|
||||
rule.sources?.length ||
|
||||
rule.moods?.length ||
|
||||
rule.stageIds?.length ||
|
||||
@@ -184,10 +186,26 @@ const mode = computed({
|
||||
|
||||
const filter = computed(() => collapseRules(props.modelValue?.rules ?? []));
|
||||
|
||||
/** پاکسازی groupIds قدیمی مشتریان هنگام باز شدن ویرایشگر */
|
||||
watch(
|
||||
() => [props.section, props.modelValue] as const,
|
||||
() => {
|
||||
if (!isCustomers.value || props.modelValue?.mode !== 'rules') return;
|
||||
const rules = props.modelValue.rules ?? [];
|
||||
if (!rules.some((r) => r.groupIds?.length)) return;
|
||||
const collapsed = collapseRules(rules);
|
||||
emit('update:modelValue', {
|
||||
mode: 'rules',
|
||||
rules: [ruleHasAnyFilter(collapsed) ? collapsed : {}],
|
||||
});
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function emitFilter(next: DataAccessRule) {
|
||||
const cleaned: DataAccessRule = {};
|
||||
if (next.owners?.length) cleaned.owners = [...next.owners];
|
||||
if (next.groupIds?.length) cleaned.groupIds = [...next.groupIds];
|
||||
if (!isCustomers.value && next.groupIds?.length) cleaned.groupIds = [...next.groupIds];
|
||||
if (next.sources?.length) cleaned.sources = [...next.sources];
|
||||
if (next.moods?.length) cleaned.moods = [...next.moods];
|
||||
if (next.stageIds?.length) cleaned.stageIds = [...next.stageIds];
|
||||
@@ -210,7 +228,6 @@ function toggleOwner(kind: DataAccessOwnerKind) {
|
||||
}
|
||||
|
||||
type ListKey =
|
||||
| 'groupIds'
|
||||
| 'sources'
|
||||
| 'moods'
|
||||
| 'stageIds'
|
||||
@@ -293,16 +310,6 @@ const moodOptions: Choice[] = PERSON_MOODS.map((mood) => ({
|
||||
const pickers = computed((): Picker[] => {
|
||||
const rule = filter.value;
|
||||
const items: Picker[] = [];
|
||||
if (showGroups.value) {
|
||||
items.push({
|
||||
key: 'groupIds',
|
||||
legend: 'گروهها',
|
||||
mode: 'multiple',
|
||||
placeholder: props.groups.length ? 'جستجو و انتخاب گروه…' : 'گروهی تعریف نشده',
|
||||
notFound: props.groups.length ? 'موردی پیدا نشد' : 'گروهی تعریف نشده',
|
||||
options: withSelected(namedOptions(props.groups), rule.groupIds),
|
||||
});
|
||||
}
|
||||
if (showImportSources.value) {
|
||||
const ready = props.importSourcesReady;
|
||||
const failed = props.importSourcesFailed;
|
||||
@@ -417,7 +424,6 @@ const filterSummary = computed(() => {
|
||||
const parts: string[] = [];
|
||||
if (rule.owners?.includes('self')) parts.push(ownerSelfLabel.value);
|
||||
if (rule.owners?.includes('team')) parts.push('تیمهای عضو');
|
||||
if (rule.groupIds?.length) parts.push(`${rule.groupIds.length} گروه`);
|
||||
if (rule.sources?.length) parts.push(`${rule.sources.length} مورد منبع/نوع`);
|
||||
if (rule.moods?.length) parts.push(`${rule.moods.length} حال`);
|
||||
if (rule.pipelineIds?.length) parts.push(`${rule.pipelineIds.length} پایپلاین`);
|
||||
@@ -446,6 +452,9 @@ const filterSummary = computed(() => {
|
||||
فقط رکوردهایی دیده میشوند که با
|
||||
<strong>همهٔ فیلترهای پرشده</strong>
|
||||
جور باشند. در هر فهرست، چند انتخاب یعنی هر کدام کافی است. خالی یعنی بدون محدودیت.
|
||||
<template v-if="isCustomers">
|
||||
اعضای گروههایی که این کاربر مسئولشان است جداگانه به همین محدوده اضافه میشوند (بدون تکرار).
|
||||
</template>
|
||||
</p>
|
||||
<p v-if="hadMultipleRules" class="hint warn">
|
||||
تنظیم قبلی چندتکه بود؛ اینجا در یک فیلتر ادغام شده. با ذخیره به همین شکل ساده ثبت میشود.
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
emptySectionDataAccess,
|
||||
} from '@mento/shared';
|
||||
import { api } from '@/lib/api';
|
||||
import { useCustomerGroups } from '@/lib/customer-groups';
|
||||
import DataAccessEditor, { type IdOption } from '@/components/DataAccessEditor.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -85,7 +84,6 @@ function setSection(key: DataAccessSection, next: SectionDataAccess) {
|
||||
});
|
||||
}
|
||||
|
||||
const { groups: customerGroups, load: loadCustomerGroups } = useCustomerGroups();
|
||||
const importSources = ref<Array<{ value: string; label: string }>>([]);
|
||||
const importSourcesReady = ref(false);
|
||||
const importSourcesFailed = ref(false);
|
||||
@@ -142,7 +140,6 @@ async function loadTreasuryAccounts() {
|
||||
}
|
||||
|
||||
async function loadOptions() {
|
||||
await loadCustomerGroups().catch(() => undefined);
|
||||
try {
|
||||
const { data } = await api.get<PersonImportSourceStatDto[]>('/people/source-stats');
|
||||
importSources.value = data.map((row) => ({ value: row.source, label: row.label }));
|
||||
@@ -362,7 +359,6 @@ const typesForActive = computed(() => {
|
||||
:key="active"
|
||||
:section="editorSection"
|
||||
:model-value="sectionValue(editorSection)"
|
||||
:groups="customerGroups"
|
||||
:stages="stagesForActive"
|
||||
:pipelines="pipelines"
|
||||
:boards="boardsForActive"
|
||||
|
||||
@@ -653,7 +653,10 @@ export const DATA_ACCESS_LEGACY_SCOPE: Record<DataAccessSection, DataScope | nul
|
||||
export type DataAccessRule = {
|
||||
/** self / team → مالک / مسئول / نویسنده (بسته به بخش) */
|
||||
owners?: DataAccessOwnerKind[];
|
||||
/** مشتریان: گروهها */
|
||||
/**
|
||||
* @deprecated دید گروه مشتریان از مسئولگروه است، نه این فیلد.
|
||||
* در پارس باقی میماند برای سازگاری؛ در فیلتر دسترسی نادیده گرفته میشود.
|
||||
*/
|
||||
groupIds?: string[];
|
||||
/** مشتریان: importSource/sourceRef · لیدها: source · یادداشت: noteType · تیکت: status */
|
||||
sources?: string[];
|
||||
|
||||
Reference in New Issue
Block a user