Polish workspace organization selector
This commit is contained in:
@@ -165,7 +165,9 @@
|
|||||||
|
|
||||||
.sidebar-workspace-menu {
|
.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;
|
@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);
|
isolation: isolate;
|
||||||
|
background: #fffdf8;
|
||||||
|
background-clip: padding-box;
|
||||||
border-color: rgba(23, 32, 51, 0.08);
|
border-color: rgba(23, 32, 51, 0.08);
|
||||||
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.12);
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.12);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
mdiChevronDown,
|
mdiChevronDown,
|
||||||
mdiCogOutline,
|
mdiCogOutline,
|
||||||
mdiPlus,
|
mdiPlus,
|
||||||
|
mdiSwapHorizontal,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
const isWorkspaceMenuOpen = ref(false);
|
const isWorkspaceMenuOpen = ref(false);
|
||||||
|
const isOrganizationListOpen = ref(false);
|
||||||
const workspaceMenuRef = ref(null);
|
const workspaceMenuRef = ref(null);
|
||||||
|
|
||||||
const activeOrganization = computed(() => organizationStore.activeOrganization);
|
const activeOrganization = computed(() => organizationStore.activeOrganization);
|
||||||
@@ -36,6 +38,11 @@
|
|||||||
});
|
});
|
||||||
const canSwitchWorkspaces = computed(() => visibleWorkspaces.value.length > 1);
|
const canSwitchWorkspaces = computed(() => visibleWorkspaces.value.length > 1);
|
||||||
const canSwitchOrganizations = computed(() => organizationStore.organizations.length > 1);
|
const canSwitchOrganizations = computed(() => organizationStore.organizations.length > 1);
|
||||||
|
const switchableOrganizations = computed(() =>
|
||||||
|
organizationStore.organizations.filter(
|
||||||
|
organization => organization.id !== organizationStore.selectedOrganizationId
|
||||||
|
)
|
||||||
|
);
|
||||||
const canManageWorkspaces = computed(() =>
|
const canManageWorkspaces = computed(() =>
|
||||||
activeOrganization.value?.currentUserPermissions?.includes(organizationPermissions.createWorkspaces) ||
|
activeOrganization.value?.currentUserPermissions?.includes(organizationPermissions.createWorkspaces) ||
|
||||||
activeOrganization.value?.currentUserPermissions?.includes(organizationPermissions.manageWorkspaces) ||
|
activeOrganization.value?.currentUserPermissions?.includes(organizationPermissions.manageWorkspaces) ||
|
||||||
@@ -57,11 +64,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
isWorkspaceMenuOpen.value = !isWorkspaceMenuOpen.value;
|
isWorkspaceMenuOpen.value = !isWorkspaceMenuOpen.value;
|
||||||
|
isOrganizationListOpen.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function chooseWorkspace(workspaceId) {
|
function chooseWorkspace(workspaceId) {
|
||||||
workspaceStore.setActiveWorkspace(workspaceId);
|
workspaceStore.setActiveWorkspace(workspaceId);
|
||||||
isWorkspaceMenuOpen.value = false;
|
isWorkspaceMenuOpen.value = false;
|
||||||
|
isOrganizationListOpen.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function chooseOrganization(organizationId) {
|
function chooseOrganization(organizationId) {
|
||||||
@@ -71,21 +80,29 @@
|
|||||||
workspace => workspace.organizationId === organizationId
|
workspace => workspace.organizationId === organizationId
|
||||||
);
|
);
|
||||||
workspaceStore.setActiveWorkspace(nextWorkspace?.id ?? null);
|
workspaceStore.setActiveWorkspace(nextWorkspace?.id ?? null);
|
||||||
|
isOrganizationListOpen.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleOrganizationList() {
|
||||||
|
isOrganizationListOpen.value = !isOrganizationListOpen.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openCreateWorkspace() {
|
async function openCreateWorkspace() {
|
||||||
isWorkspaceMenuOpen.value = false;
|
isWorkspaceMenuOpen.value = false;
|
||||||
|
isOrganizationListOpen.value = false;
|
||||||
await router.push({ name: 'workspace-create' });
|
await router.push({ name: 'workspace-create' });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openOrganizationSettings(organizationId) {
|
async function openOrganizationSettings(organizationId) {
|
||||||
isWorkspaceMenuOpen.value = false;
|
isWorkspaceMenuOpen.value = false;
|
||||||
|
isOrganizationListOpen.value = false;
|
||||||
await router.push({ name: 'organization-settings', params: { organizationId } });
|
await router.push({ name: 'organization-settings', params: { organizationId } });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDocumentClick(event) {
|
function handleDocumentClick(event) {
|
||||||
if (workspaceMenuRef.value && !workspaceMenuRef.value.contains(event.target)) {
|
if (workspaceMenuRef.value && !workspaceMenuRef.value.contains(event.target)) {
|
||||||
isWorkspaceMenuOpen.value = false;
|
isWorkspaceMenuOpen.value = false;
|
||||||
|
isOrganizationListOpen.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,36 +171,57 @@
|
|||||||
<v-icon :icon="mdiPlus" />
|
<v-icon :icon="mdiPlus" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="organization-switcher">
|
<div
|
||||||
<div class="organization-switcher-label">
|
v-if="activeOrganization"
|
||||||
<span>{{ t('workspaceSelector.organizationLabel') }}</span>
|
class="organization-switcher"
|
||||||
<strong>{{ activeOrganizationName }}</strong>
|
>
|
||||||
|
<div class="organization-current-row">
|
||||||
|
<button
|
||||||
|
class="user-menu-item organization-current"
|
||||||
|
type="button"
|
||||||
|
@click="openOrganizationSettings(activeOrganization.id)"
|
||||||
|
>
|
||||||
|
<span class="organization-mark">{{ activeOrganization.name.slice(0, 1).toUpperCase() }}</span>
|
||||||
|
<span class="user-menu-item-copy">
|
||||||
|
<span>{{ activeOrganizationName }}</span>
|
||||||
|
<small>{{ t('workspaceSelector.organizationLabel') }}</small>
|
||||||
|
</span>
|
||||||
|
<v-icon
|
||||||
|
:icon="mdiCogOutline"
|
||||||
|
class="organization-action-icon"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-for="organization in organizationStore.organizations"
|
v-if="canSwitchOrganizations"
|
||||||
:key="organization.id"
|
class="organization-swap-button"
|
||||||
class="user-menu-item organization-option"
|
|
||||||
:class="{ 'user-menu-item-active': organization.id === organizationStore.selectedOrganizationId }"
|
|
||||||
type="button"
|
type="button"
|
||||||
@click="chooseOrganization(organization.id)"
|
:aria-expanded="isOrganizationListOpen"
|
||||||
|
@click="toggleOrganizationList"
|
||||||
>
|
>
|
||||||
<span class="organization-mark">{{ organization.name.slice(0, 1).toUpperCase() }}</span>
|
<span>Change organization</span>
|
||||||
<span class="user-menu-item-copy">
|
<v-icon :icon="mdiSwapHorizontal" />
|
||||||
<span>{{ organization.name }}</span>
|
|
||||||
<small>{{ t('workspaceSelector.organizationLabel') }}</small>
|
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<div
|
||||||
v-if="activeOrganization"
|
v-if="isOrganizationListOpen"
|
||||||
class="user-menu-item organization-settings-link"
|
class="organization-options"
|
||||||
type="button"
|
|
||||||
@click="openOrganizationSettings(activeOrganization.id)"
|
|
||||||
>
|
>
|
||||||
<span>{{ t('workspaceSelector.organizationSettings') }}</span>
|
<button
|
||||||
<v-icon :icon="mdiCogOutline" />
|
v-for="organization in switchableOrganizations"
|
||||||
</button>
|
:key="organization.id"
|
||||||
|
class="user-menu-item organization-option"
|
||||||
|
type="button"
|
||||||
|
@click="chooseOrganization(organization.id)"
|
||||||
|
>
|
||||||
|
<span class="organization-mark">{{ organization.name.slice(0, 1).toUpperCase() }}</span>
|
||||||
|
<span class="user-menu-item-copy">
|
||||||
|
<span>{{ organization.name }}</span>
|
||||||
|
<small>{{ t('workspaceSelector.organizationLabel') }}</small>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -279,18 +317,18 @@
|
|||||||
border-color: rgba(23, 32, 51, 0.08);
|
border-color: rgba(23, 32, 51, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.organization-switcher-label {
|
.organization-current-row {
|
||||||
@apply flex flex-col gap-0.5 px-3 py-2;
|
@apply flex w-full min-w-0 flex-col gap-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.organization-switcher-label span {
|
.organization-current {
|
||||||
@apply text-xs font-bold uppercase tracking-[0.18em];
|
@apply w-full min-w-0 border;
|
||||||
color: #7a8799;
|
background: rgba(23, 32, 51, 0.04);
|
||||||
|
border-color: rgba(23, 32, 51, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.organization-switcher-label strong {
|
.organization-current:hover {
|
||||||
@apply truncate text-sm;
|
background: rgba(23, 32, 51, 0.07);
|
||||||
color: #172033;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.organization-mark {
|
.organization-mark {
|
||||||
@@ -299,7 +337,23 @@
|
|||||||
color: #172033;
|
color: #172033;
|
||||||
}
|
}
|
||||||
|
|
||||||
.organization-settings-link {
|
.organization-action-icon {
|
||||||
@apply justify-between;
|
@apply flex-shrink-0 text-base;
|
||||||
|
color: #526178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.organization-swap-button {
|
||||||
|
@apply flex w-full items-center justify-between gap-3 rounded-[0.9rem] border px-3 py-2.5 text-sm font-semibold transition-colors;
|
||||||
|
background: rgba(23, 32, 51, 0.04);
|
||||||
|
border-color: rgba(23, 32, 51, 0.08);
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.organization-swap-button:hover {
|
||||||
|
background: rgba(23, 32, 51, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.organization-options {
|
||||||
|
@apply flex flex-col gap-1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user