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