feat(web): refresh Montact brand and sidebar chrome

Ship the new logo assets, set primary to #10897d, and polish sidebar/home-hero UX around roles, org switcher, and the ask bar.
This commit is contained in:
2026-11-05 17:10:03 +03:30
parent 34d3689448
commit 4979aa8b75
11 changed files with 511 additions and 210 deletions
+3 -116
View File
@@ -3,10 +3,8 @@ import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
import axios from 'axios';
import { useAuthStore } from '@/stores/auth';
import { USER_ROLE_LABELS } from '@/lib/roles';
import { BUSINESS_FIELDS, BUSINESS_FIELD_LABELS } from '@/lib/business-fields';
import type { BusinessField, OrganizationMembershipDto } from '@mento/shared';
import type { OrganizationMembershipDto } from '@mento/shared';
import UiIcon from '@/components/UiIcon.vue';
import UiModal from '@/components/ui/UiModal.vue';
const props = withDefaults(
defineProps<{
@@ -28,12 +26,8 @@ const auth = useAuthStore();
const orgs = ref<OrganizationMembershipDto[]>([]);
const open = ref(false);
const createOpen = ref(false);
const loading = ref(false);
const creating = ref(false);
const error = ref('');
const newName = ref('');
const newField = ref<BusinessField>('personal');
const rootEl = ref<HTMLElement | null>(null);
const menuEl = ref<HTMLElement | null>(null);
const menuStyle = ref<Record<string, string>>({});
@@ -60,7 +54,7 @@ const currentInitial = computed(() => {
const canSwitch = computed(() => orgs.value.length > 1);
const isWorkspace = computed(() => props.variant === 'workspace');
/** در حالت workspace منو همیشه باز می‌شود (لیست org + محیط جدید)؛ در حالت پیش‌فرض فقط وقتی بیش از یکی باشد */
/** در حالت workspace منو همیشه باز می‌شود؛ در حالت پیش‌فرض فقط وقتی بیش از یکی باشد */
const menuEnabled = computed(() => isWorkspace.value || canSwitch.value);
function apiError(e: unknown, fallback: string) {
@@ -148,34 +142,6 @@ async function choose(org: OrganizationMembershipDto) {
}
}
function openCreate() {
closeMenu();
newName.value = '';
newField.value = 'personal';
error.value = '';
createOpen.value = true;
}
async function submitCreate() {
const name = newName.value.trim();
if (name.length < 2) {
error.value = 'نام محیط باید حداقل دو حرف باشد.';
return;
}
creating.value = true;
error.value = '';
try {
await auth.createOrganization({
name,
businessField: newField.value,
});
window.location.assign('/');
} catch (e) {
error.value = apiError(e, 'ساخت محیط ناموفق بود');
creating.value = false;
}
}
function onDocClick(event: MouseEvent) {
if (!open.value || Date.now() < ignoreDocClickUntil) return;
const target = event.target;
@@ -260,36 +226,9 @@ onUnmounted(() => {
:size="16"
/>
</button>
<button type="button" class="create" :disabled="loading" @click="openCreate">
محیط جدید
</button>
<p v-if="error && !createOpen" class="err">{{ error }}</p>
<p v-if="error" class="err">{{ error }}</p>
</div>
</Teleport>
<UiModal :open="createOpen" title="محیط جدید" @close="createOpen = false">
<form class="form" @submit.prevent="submitCreate">
<label>
نام محیط
<input v-model="newName" required minlength="2" placeholder="مثلاً شرکت یا کار روزمره" />
</label>
<label>
زمینه فعالیت
<select v-model="newField">
<option v-for="field in BUSINESS_FIELDS" :key="field" :value="field">
{{ BUSINESS_FIELD_LABELS[field] }}
</option>
</select>
</label>
<p v-if="error" class="err">{{ error }}</p>
</form>
<template #footer>
<button type="button" class="ghost" @click="createOpen = false">انصراف</button>
<button type="button" class="primary" :disabled="creating" @click="submitCreate">
{{ creating ? 'در حال ساخت…' : 'ساخت و ورود' }}
</button>
</template>
</UiModal>
</div>
</template>
@@ -585,61 +524,9 @@ onUnmounted(() => {
color: #0f172a;
}
.create {
margin-top: 0.15rem;
padding: 0.7rem 0.65rem 0.55rem;
border-top: 1px solid #eef2f7;
border-radius: 0 0 12px 12px;
color: var(--accent-deep);
font-weight: 700;
font-size: 0.86rem;
}
.menu.org-ws .create:hover {
background: #f8fafc;
color: #0f766e;
}
.err {
margin: 0.2rem 0 0;
color: #b91c1c;
font-size: 0.75rem;
}
.form {
display: grid;
gap: 0.75rem;
}
.form label {
display: grid;
gap: 0.35rem;
font-size: 0.88rem;
}
.form input,
.form select {
border: 1px solid var(--line);
border-radius: 10px;
padding: 0.65rem 0.75rem;
}
.ghost,
.primary {
border-radius: 10px;
padding: 0.55rem 0.9rem;
cursor: pointer;
}
.ghost {
border: 1px solid var(--line);
background: #fff;
}
.primary {
border: 0;
background: var(--accent);
color: #fff;
font-weight: 700;
}
</style>
+54 -68
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { computed } from 'vue';
import { SearchOutlined, SendOutlined } from '@/icons/ant-bridge';
import { SendOutlined } from '@/icons/ant-bridge';
import {
Button as AButton,
Input as AInput,
@@ -57,17 +57,15 @@ const statusMessage = computed(() => {
<div class="home-hero__row">
<div class="home-hero__greet">
<div class="home-hero__identity" :aria-label="firstName">
<div class="home-hero__frame">
<img
v-if="hasCustomPhoto"
:src="auth.avatarSrc"
:alt="firstName"
class="home-hero__photo"
/>
<span v-else class="home-hero__monogram" aria-hidden="true">
{{ userInitial }}
</span>
</div>
<img
v-if="hasCustomPhoto"
:src="auth.avatarSrc"
:alt="firstName"
class="home-hero__photo"
/>
<span v-else class="home-hero__monogram" aria-hidden="true">
{{ userInitial }}
</span>
<span class="home-hero__online" title="آنلاین" aria-hidden="true" />
</div>
<div class="home-hero__copy">
@@ -82,9 +80,6 @@ const statusMessage = computed(() => {
</div>
<div class="home-hero__ask">
<span class="home-hero__ask-ico" aria-hidden="true">
<SearchOutlined />
</span>
<a-input
id="home-hero-ask"
:value="askQuery"
@@ -99,13 +94,16 @@ const statusMessage = computed(() => {
<a-button
type="primary"
shape="round"
class="home-hero__ask-btn"
:disabled="asking"
:loading="asking"
aria-label="ارسال"
@click="emit('ask')"
>
<template #icon><SendOutlined /></template>
<span class="home-hero__ask-label">بپرس</span>
<span class="home-hero__ask-btn-inner">
<SendOutlined />
<span class="home-hero__ask-label">بپرس</span>
</span>
</a-button>
</div>
</div>
@@ -143,7 +141,7 @@ const statusMessage = computed(() => {
box-sizing: border-box;
overflow: hidden;
border-radius: 1rem;
border: 1px solid rgb(110 231 183 / 0.5);
border: 1px solid rgb(16 137 125 / 0.28);
box-shadow: 0 1px 2px rgb(15 23 42 / 0.04);
}
@@ -163,10 +161,10 @@ const statusMessage = computed(() => {
background:
linear-gradient(
115deg,
#ecfdf5 0%,
#d1fae5 28%,
#ccfbf1 55%,
#f0fdfa 78%,
#eef8f7 0%,
#d0ebe8 28%,
#c5e6e2 55%,
#eef8f7 78%,
#ffffff 100%
);
}
@@ -174,14 +172,14 @@ const statusMessage = computed(() => {
.home-hero__pattern {
opacity: 0.45;
background-image:
radial-gradient(circle at 1px 1px, rgba(16, 185, 129, 0.28) 1px, transparent 0),
radial-gradient(circle at 1px 1px, rgba(16, 137, 125, 0.28) 1px, transparent 0),
linear-gradient(
135deg,
rgba(20, 184, 166, 0.08) 25%,
rgba(16, 137, 125, 0.08) 25%,
transparent 25%,
transparent 50%,
rgba(20, 184, 166, 0.08) 50%,
rgba(20, 184, 166, 0.08) 75%,
rgba(16, 137, 125, 0.08) 50%,
rgba(16, 137, 125, 0.08) 75%,
transparent 75%,
transparent
);
@@ -201,7 +199,7 @@ const statusMessage = computed(() => {
top: -6rem;
width: 13rem;
height: 13rem;
background: rgb(52 211 153 / 0.2);
background: rgb(22 193 168 / 0.22);
}
.home-hero__orb--end {
@@ -209,7 +207,7 @@ const statusMessage = computed(() => {
bottom: -5rem;
width: 12rem;
height: 12rem;
background: rgb(20 184 166 / 0.15);
background: rgb(16 137 125 / 0.16);
}
.home-hero__row {
@@ -237,19 +235,8 @@ const statusMessage = computed(() => {
.home-hero__identity {
position: relative;
flex-shrink: 0;
width: 3.25rem;
height: 3.25rem;
}
.home-hero__frame {
width: 100%;
height: 100%;
border-radius: 1rem;
padding: 2px;
background: linear-gradient(145deg, #34d399 0%, #0d9488 48%, #059669 100%);
box-shadow:
0 0 0 3px rgba(255, 255, 255, 0.85),
0 8px 18px -10px rgba(4, 120, 87, 0.55);
width: 3rem;
height: 3rem;
}
.home-hero__photo,
@@ -257,33 +244,30 @@ const statusMessage = computed(() => {
display: flex;
width: 100%;
height: 100%;
border-radius: calc(1rem - 2px);
border-radius: 0.85rem;
object-fit: cover;
}
.home-hero__monogram {
align-items: center;
justify-content: center;
font-size: 1.15rem;
font-size: 1.1rem;
font-weight: 800;
color: #ecfdf5;
background:
radial-gradient(circle at 30% 25%, rgba(255, 255, 255, 0.22), transparent 45%),
linear-gradient(160deg, #10b981 0%, #0f766e 100%);
color: #eef8f7;
background: linear-gradient(160deg, #16c1a8 0%, #10897d 100%);
letter-spacing: -0.02em;
user-select: none;
}
.home-hero__online {
position: absolute;
inset-inline-start: -1px;
bottom: -1px;
width: 0.85rem;
height: 0.85rem;
inset-inline-start: 0;
bottom: 0;
width: 0.7rem;
height: 0.7rem;
border-radius: 999px;
background: #10b981;
background: #10897d;
border: 2px solid #fff;
box-shadow: 0 0 0 1px rgba(16, 185, 129, 0.25);
}
.home-hero__copy {
@@ -340,22 +324,10 @@ const statusMessage = computed(() => {
}
.home-hero__ask:focus-within {
border-color: #6ee7b7;
border-color: #16c1a8;
box-shadow:
0 0 0 2px rgb(167 243 208 / 0.7),
0 4px 12px rgb(6 78 59 / 0.05);
}
.home-hero__ask-ico {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: 2rem;
height: 2rem;
border-radius: 0.75rem;
background: #ecfdf5;
color: #059669;
0 0 0 2px rgb(16 137 125 / 0.18),
0 4px 12px rgb(10 92 84 / 0.06);
}
.home-hero__ask-input {
@@ -377,6 +349,20 @@ const statusMessage = computed(() => {
font-family: ui-sans-serif, system-ui, sans-serif;
}
.home-hero__ask-btn {
display: inline-flex !important;
align-items: center;
justify-content: center;
}
.home-hero__ask-btn-inner {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.35rem;
line-height: 1;
}
.home-hero__ask-label {
display: none;
}
+317 -16
View File
@@ -6,6 +6,7 @@ import { useAuthStore } from '@/stores/auth';
import { api } from '@/lib/api';
import { formatJalaliDateTime, formatJalaliShort, toFaDigits } from '@/lib/jalali';
import UiIcon from '@/components/UiIcon.vue';
import UserAvatar from '@/components/UserAvatar.vue';
import MentoLogo from '@/components/brand/MentoLogo.vue';
import QuickCreate from '@/components/QuickCreate.vue';
import OrgSwitcher from '@/components/OrgSwitcher.vue';
@@ -13,13 +14,15 @@ import NavHubPanel from '@/components/nav/NavHubPanel.vue';
import GamificationToaster from '@/components/gamification/GamificationToaster.vue';
import SmsHeaderWallet from '@/components/SmsHeaderWallet.vue';
import { NAV_HUBS, type NavHubDef, type NavHubKey } from '@/lib/nav-hubs';
import type {
ActivitySummaryDto,
AuthUserDto,
NotificationDto,
NotificationsListDto,
import {
brand,
type ActivitySummaryDto,
type AuthUserDto,
type NotificationDto,
type NotificationsListDto,
type OrgUserDto,
} from '@mento/shared';
import { canManageSettings, canViewAllRecords } from '@/lib/roles';
import { USER_ROLE_LABELS, canManageSettings, canViewAllRecords } from '@/lib/roles';
import { hasSectionPermission, sectionPermissionForPath } from '@/lib/section-permissions';
import { refreshUnreadTotal, useOrgChat } from '@/lib/useOrgChat';
@@ -38,8 +41,12 @@ const canSettings = computed(() => canManageSettings(auth.user?.role));
const showSettingsNav = computed(
() => canSettings.value || canOpenTeamWhileViewing.value,
);
/** فقط وقتی به‌جای نقش دیگری وارد شده‌اید؛ در پنل/دسترسی خود کاربر نشان داده نمی‌شود */
const showOwnRecordsBanner = computed(
() => Boolean(auth.user?.role) && !canViewAllRecords(auth.user?.role),
() =>
Boolean(auth.user?.viewAs) &&
Boolean(auth.user?.role) &&
!canViewAllRecords(auth.user?.role),
);
const todayLabel = computed(() => formatJalaliShort(new Date()));
@@ -260,12 +267,79 @@ const hubCanBack = computed(() => hubStack.value.length > 0);
const profileFullName = computed(() => auth.user?.fullName?.trim() || '');
const profileDisplayName = computed(() => profileFullName.value);
const profileSecondary = computed(
() => auth.user?.email?.trim() || auth.user?.mobile?.trim() || '',
const profileSecondary = computed(() => {
const mobile = auth.user?.mobile?.trim();
if (mobile) return mobile;
const email = auth.user?.email?.trim() || '';
const fromSynthetic = email.match(/^(\d+)@users\.mento\.local$/i);
return fromSynthetic?.[1] || '';
});
const roleUsers = ref<OrgUserDto[]>([]);
const roleBusy = ref(false);
const roleError = ref('');
/** سوئیچ نقش فقط برای مدیر/مالک همین محیط (یا وقتی مالک در حال مشاهده است) */
const canSwitchRoles = computed(
() => canManageSettings(auth.user?.role) || canOpenTeamWhileViewing.value,
);
function roleUserLabel(user: OrgUserDto) {
return user.orgRoleName || USER_ROLE_LABELS[user.role] || user.role;
}
const currentRoleLabel = computed(() => {
const current = roleUsers.value.find((user) => user.id === auth.user?.id);
if (current) return roleUserLabel(current);
if (auth.user?.role) return USER_ROLE_LABELS[auth.user.role];
return profileSecondary.value;
});
const sortedRoleUsers = computed(() => {
if (!canSwitchRoles.value) return [];
const me = auth.user?.id;
return [...roleUsers.value].sort((a, b) => {
if (a.id === me) return -1;
if (b.id === me) return 1;
return roleUserLabel(a).localeCompare(roleUserLabel(b), 'fa');
});
});
async function loadRoleUsers() {
if (!canSwitchRoles.value) {
roleUsers.value = [];
return;
}
try {
const { data } = await api.get<OrgUserDto[]>('/users');
// فقط کاربران فعال همین محیط (اسکوپ توکن)
roleUsers.value = data.filter((user) => user.isActive !== false);
} catch {
roleUsers.value = [];
}
}
async function switchRoleUser(user: OrgUserDto) {
if (!canSwitchRoles.value || roleBusy.value) return;
if (user.id === auth.user?.id) {
closeUserMenu();
return;
}
roleBusy.value = true;
roleError.value = '';
try {
await auth.viewAs(user.id);
closeUserMenu();
window.location.reload();
} catch {
roleError.value = 'تغییر نقش ناموفق بود.';
roleBusy.value = false;
}
}
function closeUserMenu() {
userMenuOpen.value = false;
roleError.value = '';
}
function closeOrgSwitcher() {
@@ -289,6 +363,9 @@ async function toggleUserMenu() {
userMenuOpen.value = true;
await nextTick();
placeUserMenu();
await loadRoleUsers();
await nextTick();
placeUserMenu();
}
function onOrgMenuOpen() {
@@ -656,6 +733,9 @@ onUnmounted(() => {
<div class="shell" :class="{ collapsed, 'hub-open': Boolean(openHub), 'more-open': moreOpen }">
<aside id="app-sidebar" class="sidebar" :class="{ collapsed }">
<div v-if="!collapsed" class="sidebar-logo">
<img :src="brand.logoWordmark" :alt="brand.nameFa" width="118" height="22" />
</div>
<div class="brand-row">
<div class="brand-cluster">
<OrgSwitcher
@@ -951,7 +1031,7 @@ onUnmounted(() => {
:style="userMenuStyle"
role="menu"
>
<div class="user-popover-head">
<div class="user-popover-head divided">
<span class="avatar-wrap head-avatar">
<span class="avatar">
<img
@@ -964,9 +1044,55 @@ onUnmounted(() => {
</span>
<span class="user-popover-meta">
<strong class="user-popover-name">{{ profileDisplayName }}</strong>
<small class="user-popover-sub">{{ planLabel || profileSecondary }}</small>
<small class="user-popover-sub">{{ currentRoleLabel || profileSecondary }}</small>
</span>
</div>
<div
v-if="sortedRoleUsers.length > 1"
class="user-role-list"
:class="{ collapsible: sortedRoleUsers.length > 3 }"
role="group"
aria-label="نقش‌ها"
>
<p class="user-role-hint">نقش‌ها</p>
<div class="user-role-scroll">
<button
v-for="user in sortedRoleUsers"
:key="user.id"
type="button"
class="user-role-item"
:class="{ on: user.id === auth.user?.id }"
:disabled="roleBusy"
role="menuitem"
:aria-current="user.id === auth.user?.id ? 'true' : undefined"
@click.stop="switchRoleUser(user)"
>
<UserAvatar
class="user-role-avatar"
:name="user.fullName"
:user-id="user.id"
:has-avatar="user.hasAvatar"
:size="28"
/>
<span class="user-role-copy">
<strong>{{ roleUserLabel(user) }}</strong>
<small>{{ user.fullName }}</small>
</span>
<UiIcon
v-if="user.id === auth.user?.id"
class="user-role-check"
name="check"
:size="15"
/>
</button>
</div>
</div>
<p v-if="roleError" class="user-role-err">{{ roleError }}</p>
<div
v-if="sortedRoleUsers.length > 1 || roleError"
class="user-popover-sep"
aria-hidden="true"
/>
<RouterLink
class="user-popover-item"
:to="{ name: 'profile' }"
@@ -1445,6 +1571,24 @@ onUnmounted(() => {
gap: 0.85rem;
}
.sidebar-logo {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 0.2rem 0.5rem 0.55rem;
margin-bottom: 0.2rem;
border-bottom: 1px solid #eef2f7;
}
.sidebar-logo img {
display: block;
width: auto;
max-width: 100%;
height: 22px;
object-fit: contain;
}
.brand-row {
display: flex;
align-items: center;
@@ -1501,12 +1645,13 @@ onUnmounted(() => {
align-items: center;
gap: 0.65rem;
padding: 0.58rem 0.85rem;
border: 1px solid transparent;
border-radius: 10px;
color: #475569;
text-decoration: none;
font-weight: 600;
font-size: 0.88rem;
transition: background 0.15s ease, color 0.15s ease;
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.nav-item:hover {
@@ -1515,6 +1660,7 @@ onUnmounted(() => {
}
.nav-item.router-link-active {
overflow: hidden;
background: #f3f4f6;
color: #0f172a;
box-shadow: none;
@@ -1523,6 +1669,7 @@ onUnmounted(() => {
.nav-item.router-link-active::before {
content: '';
position: absolute;
z-index: 1;
inset-inline-start: 0.2rem;
top: 50%;
transform: translateY(-50%);
@@ -1532,6 +1679,36 @@ onUnmounted(() => {
background: var(--accent);
}
.nav-item.router-link-active::after {
content: '';
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0.45;
border-radius: inherit;
background-image:
radial-gradient(circle at 1px 1px, rgba(16, 137, 125, 0.28) 1px, transparent 0),
linear-gradient(
135deg,
rgba(16, 137, 125, 0.08) 25%,
transparent 25%,
transparent 50%,
rgba(16, 137, 125, 0.08) 50%,
rgba(16, 137, 125, 0.08) 75%,
transparent 75%,
transparent
);
background-size:
18px 18px,
28px 28px;
mask-image: linear-gradient(to left, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0.25));
}
.nav-item.router-link-active > * {
position: relative;
z-index: 1;
}
.nav-item.router-link-active :deep(.ico) {
color: var(--accent-deep);
}
@@ -1542,7 +1719,8 @@ onUnmounted(() => {
box-shadow: none;
}
.shell.hub-open .nav-item.router-link-active:not(.hub-on)::before {
.shell.hub-open .nav-item.router-link-active:not(.hub-on)::before,
.shell.hub-open .nav-item.router-link-active:not(.hub-on)::after {
display: none;
}
@@ -1785,7 +1963,7 @@ onUnmounted(() => {
text-overflow: ellipsis;
white-space: nowrap;
direction: ltr;
text-align: start;
text-align: right;
}
.user-chev {
@@ -1832,7 +2010,11 @@ onUnmounted(() => {
display: flex;
align-items: center;
gap: 0.65rem;
padding: 0.4rem 0.5rem 0.55rem;
padding: 0.4rem 0.5rem 0.35rem;
}
.user-popover-head.divided {
padding-bottom: 0.55rem;
border-bottom: 1px solid #eef2f7;
margin-bottom: 0.15rem;
}
@@ -1870,6 +2052,126 @@ onUnmounted(() => {
white-space: nowrap;
}
.user-role-list {
display: grid;
gap: 0.1rem;
padding: 0.2rem 0.1rem 0.1rem;
}
.user-role-hint {
margin: 0;
padding: 0.2rem 0.55rem 0.15rem;
font-size: 0.68rem;
font-weight: 700;
color: #94a3b8;
}
.user-role-scroll {
display: grid;
gap: 0.15rem;
}
.user-role-list.collapsible .user-role-scroll {
/* دقیقاً جای ۳ ردیف؛ بقیه در همین فضا اسکرول می‌خورند */
max-height: calc(3 * (28px + 0.9rem) + 2 * 0.15rem);
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: none;
}
.user-role-list.collapsible .user-role-scroll::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
}
.user-role-item {
display: flex;
align-items: center;
gap: 0.55rem;
width: 100%;
min-height: calc(28px + 0.9rem);
border: 0;
background: transparent;
border-radius: 12px;
padding: 0.45rem 0.5rem;
color: #334155;
font: inherit;
cursor: pointer;
text-align: start;
box-sizing: border-box;
}
.user-role-item:hover:not(:disabled) {
background: #f3f4f6;
}
.user-role-item.on {
background: #f0fdfa;
}
.user-role-item:disabled {
opacity: 0.65;
cursor: wait;
}
.user-role-mark {
display: grid;
place-items: center;
width: 28px;
height: 28px;
border-radius: 9px;
flex-shrink: 0;
background: #ecfdf8;
color: #0f766e;
font-size: 0.78rem;
font-weight: 800;
}
.user-role-avatar {
flex-shrink: 0;
box-shadow: none !important;
}
.user-role-copy {
min-width: 0;
flex: 1;
display: grid;
gap: 0.05rem;
}
.user-role-copy strong {
font-size: 0.82rem;
font-weight: 700;
color: #0f172a;
line-height: 1.25;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.user-role-copy small {
font-size: 0.7rem;
font-weight: 500;
color: #94a3b8;
line-height: 1.2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.user-role-check {
color: #0f766e;
flex-shrink: 0;
}
.user-role-err {
margin: 0;
padding: 0.15rem 0.55rem 0.25rem;
font-size: 0.72rem;
color: #b91c1c;
}
.user-plan-row {
display: flex;
align-items: center;
@@ -2210,7 +2512,6 @@ onUnmounted(() => {
}
.sidebar.collapsed .nav-item.router-link-active {
background: #f3f4f6;
color: #0f172a;
}
+6 -6
View File
@@ -24,12 +24,12 @@
--line-light: #f1f5f9;
--line-strong: #cbd5e1;
/* Accent / Primary (Teal) */
--accent: #14b8a6;
--accent-hover: #0d9488;
--accent-deep: #0f766e;
--accent-soft: #ccfbf1;
--accent-subtle: #f0fdfa;
/* Accent / Primary (Montact brand) */
--accent: #10897d;
--accent-hover: #0d7369;
--accent-deep: #0a5c54;
--accent-soft: #d0ebe8;
--accent-subtle: #eef8f7;
--accent-contrast: #ffffff;
/* Semantics */
+1 -1
View File
@@ -14,7 +14,7 @@ const GRAY_200 = '#e5e7eb';
export const antdTheme = {
token: {
colorPrimary: '#10b981',
colorPrimary: '#10897d',
fontFamily: '"IRANSans", Tahoma, sans-serif',
/** Page / subtle surface fill */