refactor: align main layout shell
This commit is contained in:
@@ -1,28 +1,25 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<div class="shell-container">
|
||||
<app-bar
|
||||
:show-brand="true"
|
||||
:collapse-brand="showsAppSidebar && !isSidebarExpanded"
|
||||
/>
|
||||
<template v-if="showsAppSidebar">
|
||||
<div class="shell-sidebar-wrap">
|
||||
<app-sidebar :is-expanded="isSidebarExpanded" />
|
||||
|
||||
<button
|
||||
class="sidebar-boundary-toggle"
|
||||
type="button"
|
||||
@click="isSidebarExpanded = !isSidebarExpanded"
|
||||
>
|
||||
<v-icon :icon="isSidebarExpanded ? mdiChevronLeft : mdiChevronRight" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div
|
||||
class="shell-main"
|
||||
:class="{ 'shell-main-app': showsAppSidebar }"
|
||||
>
|
||||
<template v-if="showsAppSidebar">
|
||||
<div class="shell-sidebar-wrap">
|
||||
<app-sidebar :is-expanded="isSidebarExpanded" />
|
||||
|
||||
<button
|
||||
class="sidebar-boundary-toggle"
|
||||
type="button"
|
||||
@click="isSidebarExpanded = !isSidebarExpanded"
|
||||
>
|
||||
<v-icon :icon="isSidebarExpanded ? mdiChevronLeft : mdiChevronRight" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<app-bar v-if="showsAppSidebar" />
|
||||
|
||||
<div class="shell-view">
|
||||
<router-view></router-view>
|
||||
@@ -63,7 +60,7 @@
|
||||
|
||||
<style scoped>
|
||||
.shell-container {
|
||||
@apply min-h-screen flex flex-col;
|
||||
@apply min-h-screen flex flex-row;
|
||||
@apply w-full font-sans;
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(255, 174, 94, 0.18), transparent 28%),
|
||||
@@ -73,19 +70,19 @@
|
||||
}
|
||||
|
||||
.shell-main {
|
||||
@apply relative flex flex-1 flex-col;
|
||||
@apply relative flex min-w-0 flex-1 flex-col;
|
||||
}
|
||||
|
||||
.shell-main-app {
|
||||
@apply md:flex-row md:items-start;
|
||||
@apply min-h-screen;
|
||||
}
|
||||
|
||||
.shell-sidebar-wrap {
|
||||
@apply relative flex-shrink-0;
|
||||
@apply sticky top-0 z-30 h-screen flex-shrink-0;
|
||||
}
|
||||
|
||||
.sidebar-boundary-toggle {
|
||||
@apply absolute left-full top-8 z-10 flex h-10 w-10 -translate-x-1/2 items-center justify-center rounded-full border transition-colors;
|
||||
@apply absolute left-full top-8 z-40 flex h-10 w-10 -translate-x-1/2 items-center justify-center rounded-full border transition-colors;
|
||||
background: rgba(255, 250, 242, 0.98);
|
||||
border-color: rgba(23, 32, 51, 0.12);
|
||||
color: #44516a;
|
||||
|
||||
@@ -10,17 +10,6 @@
|
||||
mdiPlus,
|
||||
} from '@mdi/js';
|
||||
|
||||
const props = defineProps({
|
||||
showBrand: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
collapseBrand: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const authStore = useAuthStore();
|
||||
@@ -73,21 +62,6 @@
|
||||
|
||||
<template>
|
||||
<nav class="side-container">
|
||||
<div class="brand-block">
|
||||
<router-link
|
||||
v-if="showBrand"
|
||||
class="brand-link"
|
||||
:class="{ 'brand-link-collapsed': collapseBrand }"
|
||||
to="/"
|
||||
>
|
||||
<span class="brand-mark">S</span>
|
||||
<div v-if="!collapseBrand">
|
||||
<div class="brand-name">Socialize</div>
|
||||
<div class="brand-caption">{{ t('nav.brandCaption') }}</div>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div class="side-menu">
|
||||
<div class="side-menu-items side-menu-left">
|
||||
<WorkspaceSelector
|
||||
@@ -123,44 +97,15 @@
|
||||
|
||||
<style scoped>
|
||||
.side-container {
|
||||
@apply sticky top-0 z-10 flex flex-col gap-4 px-5 py-4 md:flex-row md:items-center md:justify-between;
|
||||
@apply sticky top-0 z-20 flex flex-col gap-4 px-5 py-4 md:flex-row md:items-center md:justify-between;
|
||||
background: rgba(255, 250, 242, 0.82);
|
||||
backdrop-filter: blur(18px);
|
||||
border-bottom: 1px solid rgba(23, 32, 51, 0.08);
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.brand-block {
|
||||
@apply flex items-center gap-3;
|
||||
}
|
||||
|
||||
.brand-link {
|
||||
@apply flex items-center gap-3 no-underline;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.brand-link-collapsed {
|
||||
@apply gap-0;
|
||||
}
|
||||
|
||||
.brand-mark {
|
||||
@apply flex h-11 w-11 items-center justify-center rounded-2xl text-lg font-black;
|
||||
background: linear-gradient(135deg, #ff8a3d 0%, #ef4444 100%);
|
||||
color: #fffaf2;
|
||||
}
|
||||
|
||||
.brand-name {
|
||||
@apply text-lg font-black uppercase tracking-[0.18em];
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.brand-caption {
|
||||
@apply text-xs uppercase tracking-[0.24em];
|
||||
color: #5d6b82;
|
||||
}
|
||||
|
||||
.side-menu {
|
||||
@apply flex flex-1 items-center justify-between gap-3;
|
||||
@apply flex w-full flex-1 items-center justify-between gap-3;
|
||||
}
|
||||
|
||||
.side-menu-items {
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import AppAvatar from '@/components/AppAvatar.vue';
|
||||
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
||||
import { useChannelsStore } from '@/features/channels/stores/channelsStore.js';
|
||||
import { useLanguageStore } from '@/stores/languageStore.js';
|
||||
import { useNotificationsStore } from '@/features/notifications/stores/notificationsStore.js';
|
||||
import { getNotificationRoute } from '@/features/notifications/notificationRoutes.js';
|
||||
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
|
||||
import { useCampaignsStore } from '@/features/campaigns/stores/campaignsStore.js';
|
||||
import { useUserProfileStore } from '@/features/user-profile/stores/userProfileStore.js';
|
||||
import SidebarUserMenu from './SidebarUserMenu.vue';
|
||||
import {
|
||||
mdiBellOutline,
|
||||
mdiCalendarMonthOutline,
|
||||
@@ -39,16 +37,12 @@
|
||||
const authStore = useAuthStore();
|
||||
const channelsStore = useChannelsStore();
|
||||
const contentItemsStore = useContentItemsStore();
|
||||
const languageStore = useLanguageStore();
|
||||
const notificationsStore = useNotificationsStore();
|
||||
const campaignsStore = useCampaignsStore();
|
||||
const userProfileStore = useUserProfileStore();
|
||||
const isUserMenuOpen = ref(false);
|
||||
const isNotificationsOpen = ref(false);
|
||||
const isSearchFocused = ref(false);
|
||||
const searchQuery = ref('');
|
||||
|
||||
const userMenuRef = ref(null);
|
||||
const notificationsRef = ref(null);
|
||||
const searchRef = ref(null);
|
||||
|
||||
@@ -133,25 +127,6 @@
|
||||
isNotificationsOpen.value = !isNotificationsOpen.value;
|
||||
}
|
||||
|
||||
function toggleUserMenu() {
|
||||
if (!props.isExpanded) {
|
||||
return;
|
||||
}
|
||||
|
||||
isUserMenuOpen.value = !isUserMenuOpen.value;
|
||||
}
|
||||
|
||||
function toggleLanguage() {
|
||||
const nextLocale = languageStore.locale === 'en' ? 'fr' : 'en';
|
||||
languageStore.setLocale(nextLocale);
|
||||
isUserMenuOpen.value = false;
|
||||
}
|
||||
|
||||
async function openProfile() {
|
||||
isUserMenuOpen.value = false;
|
||||
await router.push({ name: 'settings-user-information' });
|
||||
}
|
||||
|
||||
function formatNotificationTitle(notification) {
|
||||
return notificationTitleMap.value[notification.eventType] ?? notification.message;
|
||||
}
|
||||
@@ -182,11 +157,6 @@
|
||||
await router.push(result.route);
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
isUserMenuOpen.value = false;
|
||||
authStore.logout();
|
||||
}
|
||||
|
||||
function handleDocumentClick(event) {
|
||||
if (searchRef.value && !searchRef.value.contains(event.target)) {
|
||||
isSearchFocused.value = false;
|
||||
@@ -195,10 +165,6 @@
|
||||
if (isNotificationsOpen.value && notificationsRef.value && !notificationsRef.value.contains(event.target)) {
|
||||
isNotificationsOpen.value = false;
|
||||
}
|
||||
|
||||
if (isUserMenuOpen.value && userMenuRef.value && !userMenuRef.value.contains(event.target)) {
|
||||
isUserMenuOpen.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
@@ -215,15 +181,6 @@
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.isExpanded,
|
||||
isExpanded => {
|
||||
if (!isExpanded) {
|
||||
isUserMenuOpen.value = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleDocumentClick);
|
||||
});
|
||||
@@ -234,11 +191,26 @@
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside
|
||||
<aside
|
||||
class="app-sidebar"
|
||||
:class="{ 'app-sidebar-collapsed': !isExpanded }"
|
||||
>
|
||||
<div class="app-sidebar-inner">
|
||||
|
||||
<div class="brand-block">
|
||||
<router-link
|
||||
class="brand-link"
|
||||
:class="{ 'brand-link-collapsed': !isExpanded }"
|
||||
to="/"
|
||||
>
|
||||
<span class="brand-mark">S</span>
|
||||
<div v-if="isExpanded">
|
||||
<div class="brand-name">Socialize</div>
|
||||
<div class="brand-caption">{{ t('nav.brandCaption') }}</div>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div class="app-sidebar-scroll">
|
||||
<div
|
||||
v-if="authStore.isAuthenticated"
|
||||
class="sidebar-section sidebar-utilities"
|
||||
@@ -578,75 +550,52 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="authStore.isAuthenticated"
|
||||
ref="userMenuRef"
|
||||
class="sidebar-workspace sidebar-workspace-bottom"
|
||||
>
|
||||
<button
|
||||
class="sidebar-workspace-trigger"
|
||||
type="button"
|
||||
:title="!isExpanded ? userProfileStore.alias : null"
|
||||
@click.stop="toggleUserMenu"
|
||||
>
|
||||
<AppAvatar
|
||||
:name="userProfileStore.alias"
|
||||
:src="userProfileStore.portraitUrl"
|
||||
size="sm"
|
||||
/>
|
||||
<span
|
||||
v-if="isExpanded"
|
||||
class="sidebar-workspace-label"
|
||||
>
|
||||
{{ userProfileStore.alias }}
|
||||
</span>
|
||||
<v-icon
|
||||
v-if="isExpanded"
|
||||
:icon="mdiChevronDown"
|
||||
class="sidebar-workspace-icon"
|
||||
:class="{ 'sidebar-workspace-icon-open': isUserMenuOpen }"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<div
|
||||
v-if="isExpanded && isUserMenuOpen"
|
||||
class="sidebar-workspace-menu"
|
||||
>
|
||||
<button
|
||||
class="sidebar-workspace-option"
|
||||
type="button"
|
||||
@click="openProfile"
|
||||
>
|
||||
{{ t('nav.profile') }}
|
||||
</button>
|
||||
<button
|
||||
class="sidebar-workspace-option"
|
||||
type="button"
|
||||
@click="toggleLanguage"
|
||||
>
|
||||
{{ t('nav.language') }}
|
||||
</button>
|
||||
<button
|
||||
class="sidebar-workspace-option sidebar-workspace-option-danger"
|
||||
type="button"
|
||||
@click="handleLogout"
|
||||
>
|
||||
{{ t('nav.signOut') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SidebarUserMenu
|
||||
v-if="authStore.isAuthenticated"
|
||||
:is-expanded="isExpanded"
|
||||
/>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.app-sidebar {
|
||||
@apply w-[19rem] flex-shrink-0 px-4 pb-4 pt-4 transition-[width,padding] duration-200 md:sticky md:top-24 md:h-[calc(100vh-6rem)] md:pt-0;
|
||||
@apply flex h-full w-[19rem] flex-shrink-0 flex-col px-4 pt-4 transition-[width,padding] duration-200;
|
||||
border-right: 1px solid rgba(23, 32, 51, 0.08);
|
||||
}
|
||||
|
||||
.app-sidebar-inner {
|
||||
@apply flex h-full flex-col gap-4 overflow-y-auto py-3 pr-3;
|
||||
.app-sidebar-scroll {
|
||||
@apply flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto pb-4 pt-3 pr-3;
|
||||
}
|
||||
|
||||
.brand-block {
|
||||
@apply flex items-center gap-3;
|
||||
}
|
||||
|
||||
.brand-link {
|
||||
@apply flex items-center gap-3 no-underline;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.brand-mark {
|
||||
@apply flex h-11 w-11 items-center justify-center rounded-2xl text-lg font-black;
|
||||
background: linear-gradient(135deg, #ff8a3d 0%, #ef4444 100%);
|
||||
color: #fffaf2;
|
||||
}
|
||||
|
||||
.brand-name {
|
||||
@apply text-lg font-black uppercase tracking-[0.18em];
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.brand-caption {
|
||||
@apply text-xs uppercase tracking-[0.24em];
|
||||
color: #5d6b82;
|
||||
}
|
||||
|
||||
.side-menu {
|
||||
@apply flex flex-1 items-center justify-between gap-3;
|
||||
}
|
||||
|
||||
.sidebar-utilities {
|
||||
@@ -776,63 +725,6 @@
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.sidebar-workspace {
|
||||
@apply relative flex flex-col gap-2;
|
||||
}
|
||||
|
||||
.sidebar-workspace-bottom {
|
||||
@apply mt-auto pt-4;
|
||||
border-top: 1px solid rgba(23, 32, 51, 0.08);
|
||||
}
|
||||
|
||||
.sidebar-workspace-kicker {
|
||||
@apply px-4 text-[10px] font-bold uppercase tracking-[0.22em];
|
||||
color: #7a8799;
|
||||
}
|
||||
|
||||
.sidebar-workspace-trigger {
|
||||
@apply flex w-full items-center gap-3 rounded-[1.1rem] px-4 py-3 text-left transition-colors;
|
||||
background: rgba(23, 32, 51, 0.04);
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.sidebar-workspace-trigger:hover {
|
||||
background: rgba(23, 32, 51, 0.07);
|
||||
}
|
||||
|
||||
.sidebar-workspace-label {
|
||||
@apply flex-1 truncate text-sm font-semibold;
|
||||
}
|
||||
|
||||
.sidebar-workspace-icon {
|
||||
@apply text-base transition-transform;
|
||||
color: #5d6b82;
|
||||
}
|
||||
|
||||
.sidebar-workspace-icon-open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.sidebar-workspace-menu {
|
||||
@apply absolute bottom-[calc(100%+0.5rem)] left-0 right-0 z-30 flex flex-col gap-1 rounded-[1.25rem] border p-2;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border-color: rgba(23, 32, 51, 0.08);
|
||||
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.12);
|
||||
}
|
||||
|
||||
.sidebar-workspace-option {
|
||||
@apply rounded-[0.95rem] px-4 py-3 text-left text-sm font-semibold transition-colors;
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.sidebar-workspace-option:hover {
|
||||
background: rgba(23, 32, 51, 0.05);
|
||||
}
|
||||
|
||||
.sidebar-workspace-option-danger {
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.sidebar-section {
|
||||
@apply flex flex-col gap-2;
|
||||
}
|
||||
|
||||
185
frontend/src/layouts/main/SidebarUserMenu.vue
Normal file
185
frontend/src/layouts/main/SidebarUserMenu.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<script setup>
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import AppAvatar from '@/components/AppAvatar.vue';
|
||||
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
||||
import { useLanguageStore } from '@/stores/languageStore.js';
|
||||
import { useUserProfileStore } from '@/features/user-profile/stores/userProfileStore.js';
|
||||
import { mdiChevronDown } from '@mdi/js';
|
||||
|
||||
const props = defineProps({
|
||||
isExpanded: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const authStore = useAuthStore();
|
||||
const languageStore = useLanguageStore();
|
||||
const userProfileStore = useUserProfileStore();
|
||||
const isUserMenuOpen = ref(false);
|
||||
const userMenuRef = ref(null);
|
||||
|
||||
function toggleUserMenu() {
|
||||
if (!props.isExpanded) {
|
||||
return;
|
||||
}
|
||||
|
||||
isUserMenuOpen.value = !isUserMenuOpen.value;
|
||||
}
|
||||
|
||||
function toggleLanguage() {
|
||||
const nextLocale = languageStore.locale === 'en' ? 'fr' : 'en';
|
||||
languageStore.setLocale(nextLocale);
|
||||
isUserMenuOpen.value = false;
|
||||
}
|
||||
|
||||
async function openProfile() {
|
||||
isUserMenuOpen.value = false;
|
||||
await router.push({ name: 'settings-user-information' });
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
isUserMenuOpen.value = false;
|
||||
authStore.logout();
|
||||
}
|
||||
|
||||
function handleDocumentClick(event) {
|
||||
if (isUserMenuOpen.value && userMenuRef.value && !userMenuRef.value.contains(event.target)) {
|
||||
isUserMenuOpen.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.isExpanded,
|
||||
isExpanded => {
|
||||
if (!isExpanded) {
|
||||
isUserMenuOpen.value = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleDocumentClick);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('click', handleDocumentClick);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="userMenuRef"
|
||||
class="sidebar-workspace sidebar-workspace-bottom"
|
||||
>
|
||||
<button
|
||||
class="sidebar-workspace-trigger"
|
||||
type="button"
|
||||
:title="!isExpanded ? userProfileStore.alias : null"
|
||||
@click.stop="toggleUserMenu"
|
||||
>
|
||||
<AppAvatar
|
||||
:name="userProfileStore.alias"
|
||||
:src="userProfileStore.portraitUrl"
|
||||
size="sm"
|
||||
/>
|
||||
<span
|
||||
v-if="isExpanded"
|
||||
class="sidebar-workspace-label"
|
||||
>
|
||||
{{ userProfileStore.alias }}
|
||||
</span>
|
||||
<v-icon
|
||||
v-if="isExpanded"
|
||||
:icon="mdiChevronDown"
|
||||
class="sidebar-workspace-icon"
|
||||
:class="{ 'sidebar-workspace-icon-open': isUserMenuOpen }"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<div
|
||||
v-if="isExpanded && isUserMenuOpen"
|
||||
class="sidebar-workspace-menu"
|
||||
>
|
||||
<button
|
||||
class="sidebar-workspace-option"
|
||||
type="button"
|
||||
@click="openProfile"
|
||||
>
|
||||
{{ t('nav.profile') }}
|
||||
</button>
|
||||
<button
|
||||
class="sidebar-workspace-option"
|
||||
type="button"
|
||||
@click="toggleLanguage"
|
||||
>
|
||||
{{ t('nav.language') }}
|
||||
</button>
|
||||
<button
|
||||
class="sidebar-workspace-option sidebar-workspace-option-danger"
|
||||
type="button"
|
||||
@click="handleLogout"
|
||||
>
|
||||
{{ t('nav.signOut') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sidebar-workspace {
|
||||
@apply relative flex flex-col gap-2;
|
||||
}
|
||||
|
||||
.sidebar-workspace-bottom {
|
||||
@apply py-4;
|
||||
border-top: 1px solid rgba(23, 32, 51, 0.08);
|
||||
}
|
||||
|
||||
.sidebar-workspace-trigger {
|
||||
@apply flex w-full items-center gap-3 rounded-[1.1rem] px-4 py-3 text-left transition-colors;
|
||||
background: rgba(23, 32, 51, 0.04);
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.sidebar-workspace-trigger:hover {
|
||||
background: rgba(23, 32, 51, 0.07);
|
||||
}
|
||||
|
||||
.sidebar-workspace-label {
|
||||
@apply flex-1 truncate text-sm font-semibold;
|
||||
}
|
||||
|
||||
.sidebar-workspace-icon {
|
||||
@apply text-base transition-transform;
|
||||
color: #5d6b82;
|
||||
}
|
||||
|
||||
.sidebar-workspace-icon-open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.sidebar-workspace-menu {
|
||||
@apply absolute bottom-[calc(100%+0.5rem)] left-0 right-0 z-30 flex flex-col gap-1 rounded-[1.25rem] border p-2;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border-color: rgba(23, 32, 51, 0.08);
|
||||
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.12);
|
||||
}
|
||||
|
||||
.sidebar-workspace-option {
|
||||
@apply rounded-[0.95rem] px-4 py-3 text-left text-sm font-semibold transition-colors;
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.sidebar-workspace-option:hover {
|
||||
background: rgba(23, 32, 51, 0.05);
|
||||
}
|
||||
|
||||
.sidebar-workspace-option-danger {
|
||||
color: #b91c1c;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user