Improve team settings search, user sorting, and role badge layout.
EOF Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -18,8 +18,10 @@ const props = withDefaults(
|
||||
tableKey: string;
|
||||
label?: string;
|
||||
align?: 'start' | 'end';
|
||||
/** فقط آیکون تنظیمات (بدون متن «ستونها») */
|
||||
iconOnly?: boolean;
|
||||
}>(),
|
||||
{ label: 'ستونها', align: 'start' },
|
||||
{ label: 'ستونها', align: 'start', iconOnly: false },
|
||||
);
|
||||
|
||||
const open = ref(false);
|
||||
@@ -60,11 +62,18 @@ const placement = computed(() =>
|
||||
</a-button>
|
||||
</a-flex>
|
||||
</template>
|
||||
<a-button :type="hiddenCount > 0 ? 'primary' : 'default'" :ghost="hiddenCount > 0">
|
||||
<a-button
|
||||
:type="hiddenCount > 0 ? 'primary' : 'default'"
|
||||
:ghost="hiddenCount > 0"
|
||||
:title="iconOnly ? (hiddenCount ? `${label} · ${toFaDigits(hiddenCount)} مخفی` : label) : undefined"
|
||||
:aria-label="label"
|
||||
>
|
||||
<template #icon><SettingOutlined /></template>
|
||||
{{ label }}
|
||||
<template v-if="hiddenCount">
|
||||
· {{ toFaDigits(hiddenCount) }} مخفی
|
||||
<template v-if="!iconOnly">
|
||||
{{ label }}
|
||||
<template v-if="hiddenCount">
|
||||
· {{ toFaDigits(hiddenCount) }} مخفی
|
||||
</template>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-popover>
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
import { UiButton, UiEmptyState, UiField, UiInput } from '@/components/ui';
|
||||
import DataAccessPolicyPanel from '@/components/DataAccessPolicyPanel.vue';
|
||||
import { formatLocalDateInput, parseLocalInput, toFaDigits } from '@/lib/jalali';
|
||||
import { matchesSearch } from '@/lib/search';
|
||||
|
||||
const props = defineProps<{
|
||||
users: OrgUserDto[];
|
||||
@@ -74,6 +75,29 @@ const editableUsers = computed(() =>
|
||||
props.users.filter((u) => u.isActive !== false && u.role !== 'owner' && u.role !== 'admin'),
|
||||
);
|
||||
|
||||
const teamsQuery = ref('');
|
||||
const accessQuery = ref('');
|
||||
|
||||
const filteredTeams = computed(() => {
|
||||
const q = teamsQuery.value.trim();
|
||||
if (!q) return teams.value;
|
||||
return teams.value.filter((team) =>
|
||||
matchesSearch(q, [
|
||||
team.name,
|
||||
team.description,
|
||||
...team.members.map((m) => m.fullName),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
const filteredAccessUsers = computed(() => {
|
||||
const q = accessQuery.value.trim();
|
||||
if (!q) return editableUsers.value;
|
||||
return editableUsers.value.filter((u) =>
|
||||
matchesSearch(q, [u.fullName, u.mobile, u.email, u.orgRoleName, accessSummary(u)]),
|
||||
);
|
||||
});
|
||||
|
||||
async function setPriorArchive(userId: string, enabled: boolean) {
|
||||
priorSavingId.value = userId;
|
||||
priorPick.value = '';
|
||||
@@ -383,14 +407,22 @@ onMounted(() => {
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h3>تیمها</h3>
|
||||
<header class="card-head">
|
||||
<h3>تیمها</h3>
|
||||
<UiInput
|
||||
v-if="teams.length"
|
||||
v-model="teamsQuery"
|
||||
placeholder="جستجوی تیم یا عضو…"
|
||||
/>
|
||||
</header>
|
||||
<UiEmptyState
|
||||
v-if="!loading && !teams.length"
|
||||
title="تیمی تعریف نشده"
|
||||
description="تیم بسازید تا اعضای فروش پروندههای همتیمی را ببینند."
|
||||
/>
|
||||
<p v-else-if="!filteredTeams.length" class="hint">تیمی با این جستجو پیدا نشد.</p>
|
||||
<div v-else class="team-list">
|
||||
<article v-for="t in teams" :key="t.id" class="team">
|
||||
<article v-for="t in filteredTeams" :key="t.id" class="team">
|
||||
<header>
|
||||
<div>
|
||||
<strong>{{ t.name }}</strong>
|
||||
@@ -504,7 +536,14 @@ onMounted(() => {
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h3>محدوده داده</h3>
|
||||
<header class="card-head">
|
||||
<h3>محدوده داده</h3>
|
||||
<UiInput
|
||||
v-if="editableUsers.length"
|
||||
v-model="accessQuery"
|
||||
placeholder="جستجوی کاربر…"
|
||||
/>
|
||||
</header>
|
||||
<p class="hint">
|
||||
مالک و مدیر همیشه همه را میبینند. پیشفرض از نقش میآید؛ برای کاربر خاص میتوانید فیلتر
|
||||
جدا بگذارید.
|
||||
@@ -514,8 +553,9 @@ onMounted(() => {
|
||||
title="کاربر قابل تنظیم نیست"
|
||||
description="فقط نقشهای فروش و پشتیبانی محدوده داده جدا دارند."
|
||||
/>
|
||||
<p v-else-if="!filteredAccessUsers.length" class="hint">کاربری با این جستجو پیدا نشد.</p>
|
||||
<div v-else class="vis-table">
|
||||
<div v-for="u in editableUsers" :key="u.id" class="vis-user">
|
||||
<div v-for="u in filteredAccessUsers" :key="u.id" class="vis-user">
|
||||
<div class="vis-row">
|
||||
<span>
|
||||
{{ u.fullName }}
|
||||
@@ -567,6 +607,22 @@ onMounted(() => {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.card-head {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.6rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
.card-head h3 {
|
||||
margin: 0;
|
||||
}
|
||||
.card-head :deep(.field),
|
||||
.card-head :deep(input) {
|
||||
min-width: min(16rem, 100%);
|
||||
max-width: 18rem;
|
||||
}
|
||||
.prior-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user