feat(common): add foundational support for dropdown-based organization switcher (#5890)
This commit is contained in:
parent
a1be60da64
commit
faf2bfc8eb
5 changed files with 52 additions and 44 deletions
|
|
@ -2219,13 +2219,12 @@
|
||||||
"admin": "Admin"
|
"admin": "Admin"
|
||||||
},
|
},
|
||||||
"organization_sidebar": {
|
"organization_sidebar": {
|
||||||
"instances": "Instances",
|
|
||||||
"hoppscotch_cloud": "Hoppscotch Cloud",
|
"hoppscotch_cloud": "Hoppscotch Cloud",
|
||||||
"admin": "Admin",
|
"admin": "Admin",
|
||||||
"no_orgs_title": "No organizations yet",
|
|
||||||
"no_orgs_description": "Join or create an organization to collaborate with your team",
|
|
||||||
"error_loading": "Failed to load organizations",
|
"error_loading": "Failed to load organizations",
|
||||||
"inactive_orgs": "Inactive Organizations",
|
"inactive_orgs": "Inactive Organizations",
|
||||||
|
"no_orgs_found": "No organizations found",
|
||||||
|
"organizations_for": "Organizations for {email}",
|
||||||
"multi_account_notice": "Each organization keeps its own login, using the last account accessed.",
|
"multi_account_notice": "Each organization keeps its own login, using the last account accessed.",
|
||||||
"inactive_orgs_tooltip": "Contact support for assistance."
|
"inactive_orgs_tooltip": "Contact support for assistance."
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,40 @@
|
||||||
</template>
|
</template>
|
||||||
</tippy>
|
</tippy>
|
||||||
|
|
||||||
|
<!-- Organization Switcher (Web/Cloud) -->
|
||||||
|
<tippy
|
||||||
|
v-else-if="
|
||||||
|
platform.organization?.customOrganizationSwitcherComponent
|
||||||
|
"
|
||||||
|
interactive
|
||||||
|
trigger="click"
|
||||||
|
theme="popover"
|
||||||
|
:on-shown="() => orgSwitcherRef?.focus()"
|
||||||
|
:on-create="onOrgSwitcherCreate"
|
||||||
|
>
|
||||||
|
<HoppButtonSecondary
|
||||||
|
class="!font-bold uppercase tracking-wide !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark"
|
||||||
|
:label="t('app.name')"
|
||||||
|
:icon="IconChevronDown"
|
||||||
|
reverse
|
||||||
|
/>
|
||||||
|
<template #content="{ hide }">
|
||||||
|
<div
|
||||||
|
ref="orgSwitcherRef"
|
||||||
|
class="flex flex-col focus:outline-none min-w-72"
|
||||||
|
tabindex="0"
|
||||||
|
@keyup.escape="hide()"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="
|
||||||
|
platform.organization.customOrganizationSwitcherComponent
|
||||||
|
"
|
||||||
|
@close-dropdown="hide()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tippy>
|
||||||
|
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
v-else
|
v-else
|
||||||
class="!font-bold uppercase tracking-wide !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark"
|
class="!font-bold uppercase tracking-wide !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark"
|
||||||
|
|
@ -360,7 +394,9 @@ import { breakpointsTailwind, useBreakpoints, useNetwork } from "@vueuse/core"
|
||||||
import { useService } from "dioc/vue"
|
import { useService } from "dioc/vue"
|
||||||
import * as TE from "fp-ts/TaskEither"
|
import * as TE from "fp-ts/TaskEither"
|
||||||
import { pipe } from "fp-ts/function"
|
import { pipe } from "fp-ts/function"
|
||||||
|
import type { Instance } from "tippy.js"
|
||||||
import { computed, onMounted, reactive, ref, watch } from "vue"
|
import { computed, onMounted, reactive, ref, watch } from "vue"
|
||||||
|
|
||||||
import { useToast } from "~/composables/toast"
|
import { useToast } from "~/composables/toast"
|
||||||
import { GetMyTeamsQuery, TeamAccessRole } from "~/helpers/backend/graphql"
|
import { GetMyTeamsQuery, TeamAccessRole } from "~/helpers/backend/graphql"
|
||||||
import { deleteTeam as backendDeleteTeam } from "~/helpers/backend/mutations/Team"
|
import { deleteTeam as backendDeleteTeam } from "~/helpers/backend/mutations/Team"
|
||||||
|
|
@ -390,6 +426,16 @@ const downloadableLinksRef =
|
||||||
kernelMode === "web" ? ref<any | null>(null) : ref(null)
|
kernelMode === "web" ? ref<any | null>(null) : ref(null)
|
||||||
const instanceSwitcherRef =
|
const instanceSwitcherRef =
|
||||||
kernelMode === "desktop" ? ref<any | null>(null) : ref(null)
|
kernelMode === "desktop" ? ref<any | null>(null) : ref(null)
|
||||||
|
const orgSwitcherRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
// Reserve scrollbar gutter so content width doesn't shift when the list
|
||||||
|
// grows long enough to scroll inside the popover's `max-h-[45vh]` container.
|
||||||
|
const onOrgSwitcherCreate = (instance: Instance) => {
|
||||||
|
const content = instance.popper?.querySelector(".tippy-content")
|
||||||
|
if (content instanceof HTMLElement) {
|
||||||
|
content.style.scrollbarGutter = "stable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const isUserAdmin = ref(false)
|
const isUserAdmin = ref(false)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,14 +65,6 @@
|
||||||
<icon-lucide-help-circle class="svg-icons mb-4" />
|
<icon-lucide-help-circle class="svg-icons mb-4" />
|
||||||
{{ t("error.something_went_wrong") }}
|
{{ t("error.something_went_wrong") }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="showCreateOrganizationCTA" class="flex flex-col">
|
|
||||||
<hr />
|
|
||||||
<HoppButtonPrimary
|
|
||||||
:label="t('organization.create_an_organization')"
|
|
||||||
to="/orgs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<TeamsAdd
|
<TeamsAdd
|
||||||
:show="showModalAdd"
|
:show="showModalAdd"
|
||||||
|
|
@ -174,12 +166,6 @@ const isActiveWorkspace = computed(() => (id: string) => {
|
||||||
return workspace.value.teamID === id
|
return workspace.value.teamID === id
|
||||||
})
|
})
|
||||||
|
|
||||||
const showCreateOrganizationCTA = computed(() => {
|
|
||||||
const { organization } = platform
|
|
||||||
|
|
||||||
return organization?.isDefaultCloudInstance ?? false
|
|
||||||
})
|
|
||||||
|
|
||||||
const switchToTeamWorkspace = (team: GetMyTeamsQuery["myTeams"][number]) => {
|
const switchToTeamWorkspace = (team: GetMyTeamsQuery["myTeams"][number]) => {
|
||||||
REMEMBERED_TEAM_ID.value = team.id
|
REMEMBERED_TEAM_ID.value = team.id
|
||||||
workspaceService.changeWorkspace({
|
workspaceService.changeWorkspace({
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,6 @@
|
||||||
>
|
>
|
||||||
<AppSidenav />
|
<AppSidenav />
|
||||||
</Pane>
|
</Pane>
|
||||||
<Pane
|
|
||||||
v-if="showOrgSidebar"
|
|
||||||
style="width: auto; height: auto"
|
|
||||||
class="hidden !overflow-auto md:flex md:flex-col"
|
|
||||||
>
|
|
||||||
<component
|
|
||||||
:is="platform.organization.customOrganizationSidebarComponent"
|
|
||||||
/>
|
|
||||||
</Pane>
|
|
||||||
<!-- Changed to !overflow-auto to allow organization sidebar and main content to scroll independently -->
|
|
||||||
<Pane class="flex flex-1 !overflow-auto">
|
<Pane class="flex flex-1 !overflow-auto">
|
||||||
<Splitpanes
|
<Splitpanes
|
||||||
class="no-splitter"
|
class="no-splitter"
|
||||||
|
|
@ -121,14 +111,6 @@ const rootExtensionComponents = uiExtensionService.rootUIExtensionComponents
|
||||||
|
|
||||||
const HAS_OPENED_SPOTLIGHT = useSetting("HAS_OPENED_SPOTLIGHT")
|
const HAS_OPENED_SPOTLIGHT = useSetting("HAS_OPENED_SPOTLIGHT")
|
||||||
|
|
||||||
// Show organization sidebar if organization switching is enabled and sidebar component is provided
|
|
||||||
const showOrgSidebar = computed(() => {
|
|
||||||
return (
|
|
||||||
platform.organization?.organizationSwitchingEnabled === true &&
|
|
||||||
platform.organization.customOrganizationSidebarComponent
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
if (!mdAndLarger.value) {
|
if (!mdAndLarger.value) {
|
||||||
rightSidebar.value = false
|
rightSidebar.value = false
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,11 @@ export type OrganizationPlatformDef = {
|
||||||
initiateOnboarding: () => void
|
initiateOnboarding: () => void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether organization switching is enabled for this platform
|
* Custom component for the organization switcher dropdown
|
||||||
* If true, an organization switcher will be shown
|
* If provided, will be shown as a dropdown in the header (like the instance switcher)
|
||||||
|
* The component should emit 'close-dropdown' when the dropdown should close
|
||||||
*/
|
*/
|
||||||
organizationSwitchingEnabled?: boolean
|
customOrganizationSwitcherComponent?: Component
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom component for the organization sidebar
|
|
||||||
* If provided, will be shown as a sidebar in the layout
|
|
||||||
*/
|
|
||||||
customOrganizationSidebarComponent?: Component
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch to a specific organization instance or default cloud instance
|
* Switch to a specific organization instance or default cloud instance
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue