Add initial user dropdown in nav, improve typing in auth store
This commit is contained in:
parent
b20ef58727
commit
d7fc0f1b4b
|
@ -5,10 +5,10 @@
|
||||||
|
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let align = "left"
|
export let align = "left"
|
||||||
export let portalTarget
|
export let portalTarget = undefined
|
||||||
export let openOnHover = false
|
export let openOnHover = false
|
||||||
export let animate
|
export let animate = true
|
||||||
export let offset
|
export let offset = undefined
|
||||||
|
|
||||||
const actionMenuContext = getContext("actionMenu")
|
const actionMenuContext = getContext("actionMenu")
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
export let url = ""
|
export let url = ""
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let initials = "JD"
|
export let initials = "JD"
|
||||||
export let color = null
|
export let color = ""
|
||||||
|
|
||||||
const DefaultColor = "#3aab87"
|
const DefaultColor = "#3aab87"
|
||||||
|
|
||||||
|
|
|
@ -105,8 +105,8 @@
|
||||||
<img class="logo" alt="logo" src={$organisation.logoUrl || Logo} />
|
<img class="logo" alt="logo" src={$organisation.logoUrl || Logo} />
|
||||||
<ActionMenu align="right">
|
<ActionMenu align="right">
|
||||||
<div slot="control" class="avatar">
|
<div slot="control" class="avatar">
|
||||||
<UserAvatar user={$auth.user} showTooltip={false} />
|
<UserAvatar size="M" user={$auth.user} showTooltip={false} />
|
||||||
<Icon size="XL" name="ChevronDown" />
|
<Icon size="L" name="ChevronDown" />
|
||||||
</div>
|
</div>
|
||||||
<MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}>
|
<MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}>
|
||||||
My profile
|
My profile
|
||||||
|
@ -239,6 +239,7 @@
|
||||||
grid-template-columns: auto auto;
|
grid-template-columns: auto auto;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
grid-gap: var(--spacing-xs);
|
grid-gap: var(--spacing-xs);
|
||||||
|
transition: filter 130ms ease-out;
|
||||||
}
|
}
|
||||||
.avatar:hover {
|
.avatar:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
|
|
||||||
<ActionMenu align="right">
|
<ActionMenu align="right">
|
||||||
<div slot="control" class="user-dropdown">
|
<div slot="control" class="user-dropdown">
|
||||||
<UserAvatar user={$auth.user} showTooltip={false} />
|
<UserAvatar size="M" user={$auth.user} showTooltip={false} />
|
||||||
<Icon size="XL" name="ChevronDown" />
|
<Icon size="L" name="ChevronDown" />
|
||||||
</div>
|
</div>
|
||||||
<MenuItem icon="UserEdit" on:click={() => profileModal.show()}>
|
<MenuItem icon="UserEdit" on:click={() => profileModal.show()}>
|
||||||
My profile
|
My profile
|
||||||
|
@ -75,7 +75,8 @@
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-s);
|
gap: var(--spacing-xs);
|
||||||
|
transition: filter 130ms ease-out;
|
||||||
}
|
}
|
||||||
.user-dropdown:hover {
|
.user-dropdown:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, setContext } from "svelte"
|
import { getContext, setContext } from "svelte"
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
import { Heading, Icon, clickOutside, TooltipPosition } from "@budibase/bbui"
|
import { Heading, Icon, clickOutside } from "@budibase/bbui"
|
||||||
import { Constants } from "@budibase/frontend-core"
|
import { Constants } from "@budibase/frontend-core"
|
||||||
import NavItem from "./NavItem.svelte"
|
import NavItem from "./NavItem.svelte"
|
||||||
import { UserAvatar } from "@budibase/frontend-core"
|
import UserMenu from "./UserMenu.svelte"
|
||||||
|
|
||||||
const sdk = getContext("sdk")
|
const sdk = getContext("sdk")
|
||||||
const {
|
const {
|
||||||
|
@ -14,7 +14,6 @@
|
||||||
builderStore,
|
builderStore,
|
||||||
sidePanelStore,
|
sidePanelStore,
|
||||||
modalStore,
|
modalStore,
|
||||||
authStore,
|
|
||||||
} = sdk
|
} = sdk
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
const navStateStore = writable({})
|
const navStateStore = writable({})
|
||||||
|
@ -159,11 +158,6 @@
|
||||||
return !url.startsWith("http") ? `http://${url}` : url
|
return !url.startsWith("http") ? `http://${url}` : url
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigateToPortal = () => {
|
|
||||||
if ($builderStore.inBuilder) return
|
|
||||||
window.location.href = "/builder/apps"
|
|
||||||
}
|
|
||||||
|
|
||||||
const getScreenXOffset = (navigation, mobile) => {
|
const getScreenXOffset = (navigation, mobile) => {
|
||||||
if (navigation !== "Left") {
|
if (navigation !== "Left") {
|
||||||
return 0
|
return 0
|
||||||
|
@ -276,14 +270,9 @@
|
||||||
<Heading size="S" {textAlign}>{title}</Heading>
|
<Heading size="S" {textAlign}>{title}</Heading>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if !embedded && $authStore}
|
{#if !embedded}
|
||||||
<div class="user top">
|
<div class="user top">
|
||||||
<UserAvatar
|
<UserMenu compact />
|
||||||
user={$authStore}
|
|
||||||
size="M"
|
|
||||||
tooltipPosition={TooltipPosition.Left}
|
|
||||||
/>
|
|
||||||
<Icon name="ChevronDown" />
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -309,16 +298,9 @@
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if !embedded && $authStore}
|
{#if !embedded}
|
||||||
<div class="user left">
|
<div class="user left">
|
||||||
<UserAvatar user={$authStore} size="M" showTooltip={false} />
|
<UserMenu />
|
||||||
<div class="text">
|
|
||||||
<div class="name">
|
|
||||||
{$authStore.firstName}
|
|
||||||
{$authStore.lastName}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Icon name="ChevronDown" />
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -560,34 +542,6 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* User avatar */
|
|
||||||
.user {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
gap: 8px;
|
|
||||||
transition: background 130ms ease-out;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.user .text {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: center;
|
|
||||||
color: var(--navTextColor);
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.user .name {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
.layout--left .user .text {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.user:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Left overrides for both desktop and mobile */
|
/* Left overrides for both desktop and mobile */
|
||||||
.nav--left {
|
.nav--left {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { ActionMenu, Icon, MenuItem } from "@budibase/bbui"
|
||||||
|
import { UserAvatar } from "@budibase/frontend-core"
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
|
export let compact: boolean = false
|
||||||
|
|
||||||
|
const { authStore } = getContext("sdk")
|
||||||
|
|
||||||
|
const requestChangeDetails = () => {}
|
||||||
|
|
||||||
|
const requestChangePassword = () => {
|
||||||
|
// if (isOwner) {
|
||||||
|
// window.location.href = `${$admin.accountPortalUrl}/portal/account`
|
||||||
|
// } else {
|
||||||
|
// changePasswordModal.show()
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
const goToPortal = () => {
|
||||||
|
window.location.href = "/builder/apps"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $authStore}
|
||||||
|
<ActionMenu align={compact ? "right" : "left"}>
|
||||||
|
<svelte:fragment slot="control" let:open>
|
||||||
|
<div class="container" class:open>
|
||||||
|
<UserAvatar user={$authStore} size="M" showTooltip={false} />
|
||||||
|
{#if !compact}
|
||||||
|
<div class="text">
|
||||||
|
<div class="name">
|
||||||
|
{$authStore.firstName}
|
||||||
|
{$authStore.lastName}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<Icon size="L" name="ChevronDown" />
|
||||||
|
</div>
|
||||||
|
</svelte:fragment>
|
||||||
|
<MenuItem icon="UserEdit" on:click={requestChangeDetails}>
|
||||||
|
My profile
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem icon="LockClosed" on:click={requestChangePassword}>
|
||||||
|
Update password
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem icon="Apps" on:click={goToPortal}>Go to portal</MenuItem>
|
||||||
|
<MenuItem icon="LogOut" on:click={authStore.actions.logOut}>
|
||||||
|
Log out
|
||||||
|
</MenuItem>
|
||||||
|
</ActionMenu>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: var(--spacing-xs);
|
||||||
|
transition: filter 130ms ease-out;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--navTextColor);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.container:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
filter: brightness(110%);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,6 +1,7 @@
|
||||||
import ClientApp from "./components/ClientApp.svelte"
|
import ClientApp from "./components/ClientApp.svelte"
|
||||||
import UpdatingApp from "./components/UpdatingApp.svelte"
|
import UpdatingApp from "./components/UpdatingApp.svelte"
|
||||||
import {
|
import {
|
||||||
|
authStore,
|
||||||
builderStore,
|
builderStore,
|
||||||
appStore,
|
appStore,
|
||||||
blockStore,
|
blockStore,
|
||||||
|
@ -80,6 +81,7 @@ export interface SDK {
|
||||||
ActionTypes: typeof ActionTypes
|
ActionTypes: typeof ActionTypes
|
||||||
fetchDatasourceSchema: any
|
fetchDatasourceSchema: any
|
||||||
generateGoldenSample: any
|
generateGoldenSample: any
|
||||||
|
authStore: typeof authStore
|
||||||
builderStore: Readable<{
|
builderStore: Readable<{
|
||||||
inBuilder: boolean
|
inBuilder: boolean
|
||||||
}> & {
|
}> & {
|
||||||
|
|
|
@ -1,10 +1,21 @@
|
||||||
import { API } from "@/api"
|
import { API } from "@/api"
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
|
import {
|
||||||
|
AppSelfResponse,
|
||||||
|
ContextUserMetadata,
|
||||||
|
GetGlobalSelfResponse,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
type AuthState = ContextUserMetadata | GetGlobalSelfResponse | null
|
||||||
|
|
||||||
const createAuthStore = () => {
|
const createAuthStore = () => {
|
||||||
const store = writable<{
|
const store = writable<AuthState>(null)
|
||||||
csrfToken?: string
|
|
||||||
} | null>(null)
|
const hasAppSelfUser = (
|
||||||
|
user: AppSelfResponse | null
|
||||||
|
): user is ContextUserMetadata => {
|
||||||
|
return user != null && "_id" in user
|
||||||
|
}
|
||||||
|
|
||||||
// Fetches the user object if someone is logged in and has reloaded the page
|
// Fetches the user object if someone is logged in and has reloaded the page
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
|
@ -21,7 +32,10 @@ const createAuthStore = () => {
|
||||||
|
|
||||||
// Then try and get the user for this app to provide via context
|
// Then try and get the user for this app to provide via context
|
||||||
try {
|
try {
|
||||||
appSelf = await API.fetchSelf()
|
const res = await API.fetchSelf()
|
||||||
|
if (hasAppSelfUser(res)) {
|
||||||
|
appSelf = res
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Swallow
|
// Swallow
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue