Improve team settings responsive layout, narrower role panel, and mobile pagination.
EOF Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -778,5 +778,35 @@ select {
|
|||||||
.prior-picker {
|
.prior-picker {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-head {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-head :deep(.field),
|
||||||
|
.card-head :deep(input) {
|
||||||
|
max-width: none;
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team .actions {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vis-row {
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vis-badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0.25rem 0 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -184,8 +184,43 @@ function onSizeChange(e: Event) {
|
|||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.ui-pagination {
|
.ui-pagination {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: stretch;
|
||||||
gap: var(--sp-3);
|
gap: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
width: 100%;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.35rem;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-count {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-picker {
|
||||||
|
margin-inline-start: 0;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
gap: 0.25rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-picker span:last-child {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-picker select {
|
||||||
|
height: 28px;
|
||||||
|
min-width: 3.25rem;
|
||||||
|
padding: 0 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import axios from 'axios';
|
|||||||
import { api } from '@/lib/api';
|
import { api } from '@/lib/api';
|
||||||
import { useAuthStore } from '@/stores/auth';
|
import { useAuthStore } from '@/stores/auth';
|
||||||
import UiIcon from '@/components/UiIcon.vue';
|
import UiIcon from '@/components/UiIcon.vue';
|
||||||
import { UiDropdownMenu, UiModal } from '@/components/ui';
|
import { UiDropdownMenu, UiModal, UiPagination } from '@/components/ui';
|
||||||
import {
|
import {
|
||||||
DATA_ACCESS_SECTIONS,
|
DATA_ACCESS_SECTIONS,
|
||||||
ROLE_PERMISSION_ITEMS,
|
ROLE_PERMISSION_ITEMS,
|
||||||
@@ -32,13 +32,14 @@ import {
|
|||||||
formatJalaliNumeric,
|
formatJalaliNumeric,
|
||||||
toFaDigits,
|
toFaDigits,
|
||||||
} from '@/lib/jalali';
|
} from '@/lib/jalali';
|
||||||
|
import { PAGE_SIZE_OPTIONS, usePageSize } from '@/lib/pageSize';
|
||||||
import TeamsSettingsPanel from '@/components/TeamsSettingsPanel.vue';
|
import TeamsSettingsPanel from '@/components/TeamsSettingsPanel.vue';
|
||||||
import DataAccessPolicyPanel from '@/components/DataAccessPolicyPanel.vue';
|
import DataAccessPolicyPanel from '@/components/DataAccessPolicyPanel.vue';
|
||||||
|
|
||||||
type TabKey = 'users' | 'roles' | 'access' | 'teams';
|
type TabKey = 'users' | 'roles' | 'access' | 'teams';
|
||||||
type RoleColor = 'teal' | 'violet' | 'rose' | 'sky' | 'amber' | 'slate' | 'pink';
|
type RoleColor = 'teal' | 'violet' | 'rose' | 'sky' | 'amber' | 'slate' | 'pink';
|
||||||
|
|
||||||
const PAGE_SIZE = 10;
|
const pageSize = usePageSize('team.users', 10);
|
||||||
const DEFAULT_AVATAR = '/brand/user-avatar.png';
|
const DEFAULT_AVATAR = '/brand/user-avatar.png';
|
||||||
const ROLE_COLOR_OPTIONS: Array<{ value: RoleColor; label: string }> = [
|
const ROLE_COLOR_OPTIONS: Array<{ value: RoleColor; label: string }> = [
|
||||||
{ value: 'teal', label: 'سبز' },
|
{ value: 'teal', label: 'سبز' },
|
||||||
@@ -228,20 +229,23 @@ const filteredRoles = computed(() => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const pageCount = computed(() => Math.max(1, Math.ceil(filteredUsers.value.length / PAGE_SIZE)));
|
const pageCount = computed(() =>
|
||||||
|
Math.max(1, Math.ceil(filteredUsers.value.length / pageSize.value)),
|
||||||
|
);
|
||||||
|
|
||||||
const pagedUsers = computed(() => {
|
const pagedUsers = computed(() => {
|
||||||
const start = (page.value - 1) * PAGE_SIZE;
|
const start = (page.value - 1) * pageSize.value;
|
||||||
return filteredUsers.value.slice(start, start + PAGE_SIZE);
|
return filteredUsers.value.slice(start, start + pageSize.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const pageLabel = computed(() => {
|
function setPage(next: number) {
|
||||||
const total = filteredUsers.value.length;
|
page.value = next;
|
||||||
if (!total) return '۰ کاربر';
|
}
|
||||||
const from = (page.value - 1) * PAGE_SIZE + 1;
|
|
||||||
const to = Math.min(page.value * PAGE_SIZE, total);
|
function onPageSize(size: number) {
|
||||||
return `${toFaDigits(from)} تا ${toFaDigits(to)} از ${toFaDigits(total)} کاربر`;
|
pageSize.value = size;
|
||||||
});
|
page.value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
const visibleMembers = computed(() => {
|
const visibleMembers = computed(() => {
|
||||||
const role = selectedRole.value;
|
const role = selectedRole.value;
|
||||||
@@ -930,10 +934,19 @@ onUnmounted(() => {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<nav class="tabs" aria-label="بخشهای مدیریت کاربران">
|
<nav class="tabs" aria-label="بخشهای مدیریت کاربران">
|
||||||
<button type="button" :class="{ on: tab === 'users' }" @click="setTab('users')">لیست کاربران</button>
|
<button type="button" :class="{ on: tab === 'users' }" @click="setTab('users')">
|
||||||
|
<span class="tab-full">لیست کاربران</span>
|
||||||
|
<span class="tab-short">کاربران</span>
|
||||||
|
</button>
|
||||||
<button type="button" :class="{ on: tab === 'roles' }" @click="setTab('roles')">نقشها</button>
|
<button type="button" :class="{ on: tab === 'roles' }" @click="setTab('roles')">نقشها</button>
|
||||||
<button type="button" :class="{ on: tab === 'teams' }" @click="setTab('teams')">تیمها و سطح دسترسی به پروندهها</button>
|
<button type="button" :class="{ on: tab === 'teams' }" @click="setTab('teams')">
|
||||||
<button type="button" :class="{ on: tab === 'access' }" @click="setTab('access')">تنظیمات دسترسی</button>
|
<span class="tab-full">تیمها و سطح دسترسی به پروندهها</span>
|
||||||
|
<span class="tab-short">تیمها و دسترسی</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" :class="{ on: tab === 'access' }" @click="setTab('access')">
|
||||||
|
<span class="tab-full">تنظیمات دسترسی</span>
|
||||||
|
<span class="tab-short">دسترسی</span>
|
||||||
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<p v-if="error" class="error">{{ error }}</p>
|
<p v-if="error" class="error">{{ error }}</p>
|
||||||
@@ -1228,25 +1241,25 @@ onUnmounted(() => {
|
|||||||
<table v-table-cols="'team.users'">
|
<table v-table-cols="'team.users'">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>نام و نام خانوادگی</th>
|
<th class="col-who">نام و نام خانوادگی</th>
|
||||||
<th>موبایل</th>
|
<th class="col-mobile">موبایل</th>
|
||||||
<th class="col-role">نقش</th>
|
<th class="col-role">نقش</th>
|
||||||
<th>پورسانت %</th>
|
<th class="col-comm">پورسانت %</th>
|
||||||
<th>وضعیت</th>
|
<th class="col-status">وضعیت</th>
|
||||||
<th>آخرین ورود</th>
|
<th class="col-login">آخرین ورود</th>
|
||||||
<th v-if="canSetLogoutTime">ساعت خروج</th>
|
<th v-if="canSetLogoutTime" class="col-logout">ساعت خروج</th>
|
||||||
<th class="ops" />
|
<th class="ops" />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="user in pagedUsers" :key="user.id" @click="onUserActivate(user)">
|
<tr v-for="user in pagedUsers" :key="user.id" @click="onUserActivate(user)">
|
||||||
<td>
|
<td class="col-who">
|
||||||
<div class="who">
|
<div class="who">
|
||||||
<img :src="avatarOf(user.id)" :alt="user.fullName" />
|
<img :src="avatarOf(user.id)" :alt="user.fullName" />
|
||||||
<strong>{{ user.fullName }}</strong>
|
<strong class="who-name" :title="user.fullName">{{ user.fullName }}</strong>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="muted" dir="ltr">{{ user.mobile || '—' }}</td>
|
<td class="col-mobile muted" dir="ltr">{{ user.mobile || '—' }}</td>
|
||||||
<td class="col-role">
|
<td class="col-role">
|
||||||
<span
|
<span
|
||||||
class="pill"
|
class="pill"
|
||||||
@@ -1256,7 +1269,7 @@ onUnmounted(() => {
|
|||||||
{{ user.orgRoleName || '—' }}
|
{{ user.orgRoleName || '—' }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td @click.stop>
|
<td class="col-comm" @click.stop>
|
||||||
<label class="comm-field">
|
<label class="comm-field">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
@@ -1271,7 +1284,7 @@ onUnmounted(() => {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="col-status">
|
||||||
<span class="status" :class="{ off: user.isActive === false }">
|
<span class="status" :class="{ off: user.isActive === false }">
|
||||||
<i />
|
<i />
|
||||||
{{ user.isActive === false ? 'غیرفعال' : 'فعال' }}
|
{{ user.isActive === false ? 'غیرفعال' : 'فعال' }}
|
||||||
@@ -1284,8 +1297,8 @@ onUnmounted(() => {
|
|||||||
ورود با کد
|
ورود با کد
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="muted">{{ formatLastLogin(user.lastLoginAt) }}</td>
|
<td class="col-login muted">{{ formatLastLogin(user.lastLoginAt) }}</td>
|
||||||
<td v-if="canSetLogoutTime" @click.stop>
|
<td v-if="canSetLogoutTime" class="col-logout" @click.stop>
|
||||||
<span v-if="user.role === 'owner'" class="muted">—</span>
|
<span v-if="user.role === 'owner'" class="muted">—</span>
|
||||||
<span v-else class="logout-cell">
|
<span v-else class="logout-cell">
|
||||||
<button
|
<button
|
||||||
@@ -1360,55 +1373,69 @@ onUnmounted(() => {
|
|||||||
<article v-for="user in pagedUsers" :key="user.id" class="user-card" @click="onUserActivate(user)">
|
<article v-for="user in pagedUsers" :key="user.id" class="user-card" @click="onUserActivate(user)">
|
||||||
<div class="who">
|
<div class="who">
|
||||||
<img :src="avatarOf(user.id)" :alt="user.fullName" />
|
<img :src="avatarOf(user.id)" :alt="user.fullName" />
|
||||||
<div>
|
<strong class="who-name" :title="user.fullName">{{ user.fullName }}</strong>
|
||||||
<strong>{{ user.fullName }}</strong>
|
<span
|
||||||
<span
|
class="pill"
|
||||||
class="pill"
|
:data-color="user.orgRoleColor || 'teal'"
|
||||||
:data-color="user.orgRoleColor || 'teal'"
|
:title="user.orgRoleName || undefined"
|
||||||
:title="user.orgRoleName || undefined"
|
>
|
||||||
|
{{ user.orgRoleName || '—' }}
|
||||||
|
</span>
|
||||||
|
<div class="card-ops" @click.stop>
|
||||||
|
<UiDropdownMenu
|
||||||
|
:open="menuUserId === `m:${user.id}`"
|
||||||
|
align="end"
|
||||||
|
@update:open="(v) => (menuUserId = v ? `m:${user.id}` : null)"
|
||||||
>
|
>
|
||||||
{{ user.orgRoleName || '—' }}
|
<template #trigger="{ toggle }">
|
||||||
</span>
|
<button type="button" class="ghost-ico" aria-label="عملیات" @click.stop="toggle">
|
||||||
<p v-if="user.mobile" class="muted mobile-line" dir="ltr">{{ user.mobile }}</p>
|
<UiIcon name="dots-v" :size="16" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<button
|
||||||
|
v-if="canInvite"
|
||||||
|
type="button"
|
||||||
|
@click="openEditUser(user)"
|
||||||
|
>
|
||||||
|
ویرایش مشخصات
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="canInvite && user.role !== 'owner'"
|
||||||
|
type="button"
|
||||||
|
:class="{ on: user.loginOtpOnly }"
|
||||||
|
:title="LOGIN_OTP_ONLY_TIP"
|
||||||
|
@click="toggleLoginOtpOnly(user)"
|
||||||
|
>
|
||||||
|
{{ user.loginOtpOnly ? 'لغو ورود با کد' : 'ورود با کد' }}
|
||||||
|
</button>
|
||||||
|
<button type="button" @click="toggleStatus(user)">
|
||||||
|
{{ user.isActive === false ? 'فعال کردن' : 'غیرفعال کردن' }}
|
||||||
|
</button>
|
||||||
|
</UiDropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-meta" @click.stop>
|
<div class="card-meta" @click.stop>
|
||||||
<label class="comm-field">
|
<div class="card-meta-row">
|
||||||
<span>پورسانت %</span>
|
<p class="muted mobile-line" dir="ltr">{{ user.mobile || '—' }}</p>
|
||||||
<input
|
<label class="comm-field">
|
||||||
type="number"
|
<span>پورسانت %</span>
|
||||||
min="0"
|
<input
|
||||||
max="100"
|
type="number"
|
||||||
step="0.1"
|
min="0"
|
||||||
dir="ltr"
|
max="100"
|
||||||
:value="user.commissionPercent ?? 0"
|
step="0.1"
|
||||||
:disabled="!canInvite"
|
dir="ltr"
|
||||||
:aria-label="`پورسانت ${user.fullName}`"
|
:value="user.commissionPercent ?? 0"
|
||||||
@change="changeCommission(user, ($event.target as HTMLInputElement).value)"
|
:disabled="!canInvite"
|
||||||
/>
|
:aria-label="`پورسانت ${user.fullName}`"
|
||||||
</label>
|
@change="changeCommission(user, ($event.target as HTMLInputElement).value)"
|
||||||
<span class="status" :class="{ off: user.isActive === false }">
|
/>
|
||||||
<i />
|
</label>
|
||||||
{{ user.isActive === false ? 'غیرفعال' : 'فعال' }}
|
<span class="status" :class="{ off: user.isActive === false }">
|
||||||
</span>
|
<i />
|
||||||
<button
|
{{ user.isActive === false ? 'غیرفعال' : 'فعال' }}
|
||||||
v-if="canInvite"
|
</span>
|
||||||
type="button"
|
</div>
|
||||||
class="btn ghost"
|
|
||||||
@click="openEditUser(user)"
|
|
||||||
>
|
|
||||||
ویرایش
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="canInvite && user.role !== 'owner'"
|
|
||||||
type="button"
|
|
||||||
class="btn ghost"
|
|
||||||
:class="{ 'otp-on': user.loginOtpOnly }"
|
|
||||||
:title="LOGIN_OTP_ONLY_TIP"
|
|
||||||
@click="toggleLoginOtpOnly(user)"
|
|
||||||
>
|
|
||||||
{{ user.loginOtpOnly ? 'لغو ورود با کد' : 'ورود با کد' }}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<p v-if="!loading && !pagedUsers.length" class="empty">کاربری یافت نشد.</p>
|
<p v-if="!loading && !pagedUsers.length" class="empty">کاربری یافت نشد.</p>
|
||||||
@@ -1439,22 +1466,16 @@ onUnmounted(() => {
|
|||||||
<p v-if="!loading && !filteredRoles.length" class="empty">نقشی با این جستجو پیدا نشد.</p>
|
<p v-if="!loading && !filteredRoles.length" class="empty">نقشی با این جستجو پیدا نشد.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer v-if="tab === 'users' && filteredUsers.length" class="pager">
|
<UiPagination
|
||||||
<span>{{ pageLabel }}</span>
|
v-if="tab === 'users' && filteredUsers.length"
|
||||||
<div class="pages">
|
:page="page"
|
||||||
<button type="button" :disabled="page <= 1" @click="page -= 1">‹</button>
|
:total-pages="pageCount"
|
||||||
<button
|
:total-items="filteredUsers.length"
|
||||||
v-for="n in pageCount"
|
:page-size="pageSize"
|
||||||
:key="n"
|
:page-size-options="PAGE_SIZE_OPTIONS"
|
||||||
type="button"
|
@update:page="setPage"
|
||||||
:class="{ on: n === page }"
|
@update:page-size="onPageSize"
|
||||||
@click="page = n"
|
/>
|
||||||
>
|
|
||||||
{{ toFaDigits(n) }}
|
|
||||||
</button>
|
|
||||||
<button type="button" :disabled="page >= pageCount" @click="page += 1">›</button>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1733,8 +1754,12 @@ h1 {
|
|||||||
.tabs {
|
.tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
gap: 1.75rem;
|
gap: 1.25rem;
|
||||||
border-bottom: 1px solid #e2e8f0;
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
overscroll-behavior-x: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs button {
|
.tabs button {
|
||||||
@@ -1748,6 +1773,8 @@ h1 {
|
|||||||
font: inherit;
|
font: inherit;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs button.on {
|
.tabs button.on {
|
||||||
@@ -1755,11 +1782,15 @@ h1 {
|
|||||||
border-bottom-color: #14b8a6;
|
border-bottom-color: #14b8a6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab-short {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.stage {
|
.stage {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(280px, 340px);
|
grid-template-columns: minmax(0, 1fr) 220px;
|
||||||
grid-template-areas: 'list panel';
|
grid-template-areas: 'list panel';
|
||||||
gap: 1rem;
|
gap: 0.85rem;
|
||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1925,25 +1956,30 @@ h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-card {
|
.table-card {
|
||||||
overflow: hidden;
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
table-layout: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
th,
|
th,
|
||||||
td {
|
td {
|
||||||
padding: 0.85rem 1rem;
|
padding: 0.55rem 0.55rem;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
font-size: 0.9rem;
|
font-size: 0.88rem;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
thead th {
|
thead th {
|
||||||
color: #94a3b8;
|
color: #94a3b8;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-bottom: 1px solid #f1f5f9;
|
border-bottom: 1px solid #f1f5f9;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr {
|
tbody tr {
|
||||||
@@ -1955,27 +1991,105 @@ tbody tr + tr td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ops {
|
.ops {
|
||||||
width: 44px;
|
width: 2.5rem;
|
||||||
padding-inline: 0.4rem;
|
padding-inline: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-who {
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-inline-start: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-mobile {
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-role {
|
||||||
|
width: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-comm {
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-status {
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-login {
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-logout {
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.who {
|
.who {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
max-width: 14rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.who-text {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.3rem;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.who-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.7rem;
|
justify-content: space-between;
|
||||||
|
gap: 0.5rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.who-title .who-name {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.who-title .pill {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.who img,
|
.who img,
|
||||||
.members img {
|
.members img {
|
||||||
width: 40px;
|
flex: 0 0 36px;
|
||||||
height: 40px;
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
min-width: 36px;
|
||||||
|
min-height: 36px;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
object-position: center;
|
||||||
background: #e2e8f0;
|
background: #e2e8f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.who-name,
|
||||||
.who strong {
|
.who strong {
|
||||||
display: block;
|
display: block;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 11rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 1.35;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #0f172a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pill {
|
.pill {
|
||||||
@@ -1983,9 +2097,9 @@ tbody tr + tr td {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 0.2rem 0.5rem;
|
padding: 0.15rem 0.45rem;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
font-size: 0.78rem;
|
font-size: 0.76rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: #ccfbf1;
|
background: #ccfbf1;
|
||||||
@@ -1993,12 +2107,17 @@ tbody tr + tr td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-card .col-role {
|
.table-card .col-role {
|
||||||
width: 6.5rem;
|
|
||||||
max-width: 6.5rem;
|
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-card .pill,
|
.table-card .pill {
|
||||||
|
width: max-content;
|
||||||
|
max-width: 6.25rem;
|
||||||
|
white-space: normal;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
.user-card .pill {
|
.user-card .pill {
|
||||||
width: max-content;
|
width: max-content;
|
||||||
max-width: 5.75rem;
|
max-width: 5.75rem;
|
||||||
@@ -2007,6 +2126,25 @@ tbody tr + tr td {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-card .comm-field {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card .comm-field input {
|
||||||
|
width: 3.6rem;
|
||||||
|
min-height: 2rem;
|
||||||
|
padding: 0 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card .col-status .status,
|
||||||
|
.table-card .col-status .otp-badge {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card .otp-badge {
|
||||||
|
margin-inline-start: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
.pill[data-color='violet'] {
|
.pill[data-color='violet'] {
|
||||||
background: #f3e8ff;
|
background: #f3e8ff;
|
||||||
color: #7c3aed;
|
color: #7c3aed;
|
||||||
@@ -2085,8 +2223,11 @@ tbody tr + tr td {
|
|||||||
top: 0.75rem;
|
top: 0.75rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 34rem;
|
width: 220px;
|
||||||
padding: 1rem 1.05rem 1.1rem;
|
max-width: 220px;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 0.85rem 0.75rem 0.95rem;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-head,
|
.panel-head,
|
||||||
@@ -2104,17 +2245,18 @@ tbody tr + tr td {
|
|||||||
.panel-head h2,
|
.panel-head h2,
|
||||||
.role-hero h3 {
|
.role-hero h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 0.95rem;
|
font-size: 0.88rem;
|
||||||
color: #0d9488;
|
color: #0d9488;
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-hero {
|
.role-hero {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin: 1rem 0 1.15rem;
|
margin: 0.75rem 0 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-hero h3 {
|
.role-hero h3 {
|
||||||
font-size: 1.05rem;
|
font-size: 0.95rem;
|
||||||
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-hero p,
|
.role-hero p,
|
||||||
@@ -2122,17 +2264,17 @@ tbody tr + tr td {
|
|||||||
.role-card p {
|
.role-card p {
|
||||||
margin: 0.25rem 0 0;
|
margin: 0.25rem 0 0;
|
||||||
color: #94a3b8;
|
color: #94a3b8;
|
||||||
font-size: 0.82rem;
|
font-size: 0.78rem;
|
||||||
line-height: 1.7;
|
line-height: 1.55;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-block + .panel-block {
|
.panel-block + .panel-block {
|
||||||
margin-top: 1.15rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-block h4 {
|
.panel-block h4 {
|
||||||
margin: 0 0 0.7rem;
|
margin: 0 0 0.55rem;
|
||||||
font-size: 0.92rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.perms,
|
.perms,
|
||||||
@@ -2141,15 +2283,15 @@ tbody tr + tr td {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.55rem;
|
gap: 0.45rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.perms li,
|
.perms li,
|
||||||
.members li {
|
.members li {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.55rem;
|
gap: 0.4rem;
|
||||||
font-size: 0.88rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.perm-check {
|
.perm-check {
|
||||||
@@ -2212,7 +2354,8 @@ tbody tr + tr td {
|
|||||||
|
|
||||||
.btn.add {
|
.btn.add {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
order: 2;
|
order: 4;
|
||||||
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.outline {
|
.btn.outline {
|
||||||
@@ -2238,36 +2381,8 @@ tbody tr + tr td {
|
|||||||
color: #334155;
|
color: #334155;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pager {
|
.main-col :deep(.ui-pagination) {
|
||||||
display: flex;
|
margin-top: 0.75rem;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.75rem;
|
|
||||||
padding: 0.85rem 0.2rem 0;
|
|
||||||
color: #94a3b8;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pages {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.35rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pages button {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: transparent;
|
|
||||||
color: #64748b;
|
|
||||||
font: inherit;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pages button.on {
|
|
||||||
background: #ccfbf1;
|
|
||||||
color: #0f766e;
|
|
||||||
font-weight: 800;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-list {
|
.mobile-list {
|
||||||
@@ -2276,26 +2391,93 @@ tbody tr + tr td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-card {
|
.user-card {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.55rem;
|
||||||
|
padding: 0.9rem 1rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-card .who {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.75rem;
|
gap: 0.45rem;
|
||||||
padding: 0.85rem 1rem;
|
max-width: none;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-card .who-name {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 1.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-card .pill {
|
.user-card .pill {
|
||||||
margin-top: 0.35rem;
|
flex: 0 1 auto;
|
||||||
|
max-width: 7.5rem;
|
||||||
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-line {
|
.user-card .card-ops {
|
||||||
margin: 0.2rem 0 0;
|
flex: 0 0 auto;
|
||||||
font-size: 0.78rem;
|
margin-inline-start: -0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-card .card-ops .ghost-ico {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-meta {
|
.card-meta {
|
||||||
|
padding-top: 0.55rem;
|
||||||
|
margin-top: 0.15rem;
|
||||||
|
border-top: 1px solid #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-meta-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
justify-items: end;
|
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||||||
gap: 0.45rem;
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-meta-row .mobile-line {
|
||||||
|
margin: 0;
|
||||||
|
justify-self: start;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.3;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-meta-row .comm-field {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-self: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-meta-row .comm-field input {
|
||||||
|
width: 3.4rem;
|
||||||
|
min-height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-meta-row .status {
|
||||||
|
justify-self: end;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comm-field {
|
.comm-field {
|
||||||
@@ -2574,14 +2756,36 @@ textarea {
|
|||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1100px) {
|
@media (max-width: 1200px) {
|
||||||
.stage {
|
.stage {
|
||||||
grid-template-columns: minmax(0, 1fr) 280px;
|
grid-template-columns: minmax(0, 1fr) 220px;
|
||||||
grid-template-areas: 'list panel';
|
grid-template-areas: 'list panel';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.who {
|
||||||
|
max-width: 12rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.who-name,
|
||||||
|
.who strong {
|
||||||
|
max-width: 9.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
|
.tab-full {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-short {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
gap: 1rem;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
.stage {
|
.stage {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
grid-template-areas: 'list';
|
grid-template-areas: 'list';
|
||||||
@@ -2603,37 +2807,54 @@ textarea {
|
|||||||
|
|
||||||
.stats {
|
.stats {
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats article {
|
.stats article {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.35rem;
|
gap: 0.3rem;
|
||||||
padding: 0.35rem 0.55rem;
|
padding: 0.4rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats p {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats strong {
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-ico {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
gap: 0.55rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
flex: 1 1 100%;
|
flex: 1 1 100%;
|
||||||
order: 4;
|
order: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-filter {
|
.role-filter {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 0;
|
||||||
order: 2;
|
order: 2;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-cols {
|
.toolbar-cols {
|
||||||
order: 3;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.add {
|
.btn.add {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
order: 1;
|
order: 3;
|
||||||
|
padding-inline: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.org-grid,
|
.org-grid,
|
||||||
@@ -2642,6 +2863,15 @@ textarea {
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.roles-tree .role-card {
|
||||||
|
margin-inline-start: calc(var(--role-depth, 0) * 0.65rem);
|
||||||
|
padding: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-card-top {
|
||||||
|
gap: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
.matrix-scroll {
|
.matrix-scroll {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -2734,10 +2964,13 @@ textarea {
|
|||||||
bottom: var(--mobile-tabbar-h, 64px);
|
bottom: var(--mobile-tabbar-h, 64px);
|
||||||
top: auto;
|
top: auto;
|
||||||
z-index: 71;
|
z-index: 71;
|
||||||
|
width: auto;
|
||||||
|
max-width: none;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
max-height: min(78dvh, 640px);
|
max-height: min(78dvh, 640px);
|
||||||
|
overflow: auto;
|
||||||
border-radius: 22px 22px 0 0;
|
border-radius: 22px 22px 0 0;
|
||||||
padding-bottom: 0.85rem;
|
padding: 0.85rem 1rem 0.85rem;
|
||||||
animation: sheet-up 0.22s ease;
|
animation: sheet-up 0.22s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2757,8 +2990,23 @@ textarea {
|
|||||||
padding-top: 0.75rem;
|
padding-top: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pager {
|
}
|
||||||
flex-direction: column;
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.page {
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-chips {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-card .pill {
|
||||||
|
max-width: 6.25rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user