Browse Source

fix(nocohub): Added profile page, added xlarge type to usericon, added useUsers store and now call me api on app startup and overwrite things set by jwt payload, made profile page the default

pull/6376/head
Muhammed Mustafa 1 year ago
parent
commit
12ca937e4c
  1. 113
      packages/nc-gui/components/account/Profile.vue
  2. 4
      packages/nc-gui/components/dashboard/Sidebar/UserInfo.vue
  3. 30
      packages/nc-gui/components/general/UserIcon.vue
  4. 1
      packages/nc-gui/composables/useGlobal/actions.ts
  5. 1
      packages/nc-gui/composables/useGlobal/index.ts
  6. 1
      packages/nc-gui/lang/en.json
  7. 1
      packages/nc-gui/lib/types.ts
  8. 65
      packages/nc-gui/store/users.ts

113
packages/nc-gui/components/account/Profile.vue

@ -0,0 +1,113 @@
<script lang="ts" setup>
const { currentUser } = storeToRefs(useUsers())
const isErrored = ref(false)
const isTitleUpdating = ref(false)
const form = ref({
title: '',
email: '',
})
const { updateUserProfile } = useUsers()
const formValidator = ref()
const formRules = {
title: [
{ required: true, message: 'Name required' },
{ min: 2, message: 'Name must be at least 2 characters long' },
{ max: 60, message: 'Name must be at most 60 characters long' },
],
}
const onSubmit = async () => {
const valid = await formValidator.value.validate()
if (!valid) return
if (isTitleUpdating.value) return
isTitleUpdating.value = true
isErrored.value = false
try {
await updateUserProfile({ attrs: { display_name: form.value.title } })
} catch (e: any) {
console.error(e)
} finally {
isTitleUpdating.value = false
}
}
watch(
() => currentUser.value?.display_name,
() => {
if (!currentUser.value?.display_name) return
form.value.title = currentUser.value.display_name
form.value.email = currentUser.value.email
},
{
immediate: true,
},
)
watch(
() => form.value.title,
async () => {
try {
isErrored.value = !(await formValidator.value.validate())
} catch (e: any) {
isErrored.value = true
}
},
)
</script>
<template>
<div class="flex flex-col items-center">
<div class="flex flex-col w-150">
<div class="flex font-medium text-xl">Profile</div>
<div class="mt-5 flex flex-col border-1 rounded-2xl border-gray-200 p-6 gap-y-2">
<div class="flex font-medium text-base">Account details</div>
<div class="flex text-gray-500">Control your appearance.</div>
<div class="flex flex-row mt-4 gap-x-8">
<div class="flex h-20">
<GeneralUserIcon size="xlarge" />
</div>
<a-form ref="formValidator" layout="vertical" no-style :model="form" class="w-full" @finish="onSubmit">
<div class="text-gray-800 mb-1.5">Name</div>
<a-form-item name="title" :rules="formRules.title">
<a-input
v-model:value="form.title"
class="w-full !rounded-md !py-1.5"
placeholder="Name"
data-testid="nc-account-settings-rename-input"
/>
</a-form-item>
<div class="text-gray-800 mb-1.5">Account Email ID</div>
<a-input
v-model:value="form.email"
class="w-full !rounded-md !py-1.5"
placeholder="Email"
:disabled="true"
data-testid="nc-account-settings-email-input"
/>
<div class="flex flex-row w-full justify-end mt-8">
<NcButton
type="primary"
html-type="submit"
:disabled="isErrored || (form.title && form.title === currentUser?.display_name)"
:loading="isTitleUpdating"
data-testid="nc-account-settings-save"
@click="onSubmit"
>
<template #loading> Saving </template>
Save
</NcButton>
</div>
</a-form>
</div>
</div>
</div>
</div>
</template>

4
packages/nc-gui/components/dashboard/Sidebar/UserInfo.vue

@ -9,7 +9,7 @@ const { leftSidebarState } = storeToRefs(useSidebarStore())
const { copy } = useCopy(true)
const name = computed(() => `${user.value?.firstname ?? ''} ${user.value?.lastname ?? ''}`.trim())
const name = computed(() => user.value?.display_name?.trim())
const isMenuOpen = ref(false)
@ -133,7 +133,7 @@ onMounted(() => {
<template v-if="isAuthTokenCopied"> Copied Auth Token </template>
<template v-else> Copy Auth Token </template>
</NcMenuItem>
<nuxt-link v-e="['c:navbar:user:email']" class="!no-underline" to="/account/tokens">
<nuxt-link v-e="['c:navbar:user:email']" class="!no-underline" to="/account/profile">
<NcMenuItem><GeneralIcon icon="settings" class="menu-icon" /> Account Settings</NcMenuItem>
</nuxt-link>
</NcMenu>

30
packages/nc-gui/components/general/UserIcon.vue

@ -1,26 +1,26 @@
<script lang="ts" setup>
const props = defineProps<{
hideLabel?: boolean
size?: 'small' | 'medium'
size?: 'small' | 'medium' | 'large' | 'xlarge'
}>()
const { user } = useGlobal()
const { currentUser } = storeToRefs(useUsers())
const backgroundColor = computed(() => (user.value?.id ? stringToColour(user.value?.id) : '#FFFFFF'))
const backgroundColor = computed(() => (currentUser.value?.id ? stringToColour(currentUser.value?.id) : '#FFFFFF'))
const size = computed(() => props.size || 'medium')
const firstName = computed(() => user.value?.firstname ?? '')
const lastName = computed(() => user.value?.lastname ?? '')
const email = computed(() => user.value?.email ?? '')
const displayName = computed(() => currentUser.value?.display_name ?? '')
const email = computed(() => currentUser.value?.email ?? '')
const usernameInitials = computed(() => {
if (firstName.value && lastName.value) {
return firstName.value[0] + lastName.value[0]
} else if (firstName.value) {
return firstName.value[0] + (firstName.value.length > 1 ? firstName.value[1] : '')
} else if (lastName.value) {
return lastName.value[0] + (lastName.value.length > 1 ? lastName.value[1] : '')
if (displayName.value) {
const displayNameSplit = displayName.value.split(' ')
if (displayNameSplit.length > 1) {
return displayNameSplit[0][0] + displayNameSplit[1][0]
} else {
return displayName.value[0] + displayName.value[1]
}
} else {
return email.value[0] + email.value[1]
}
@ -33,12 +33,12 @@ const usernameInitials = computed(() => {
:class="{
'min-w-4 min-h-4': size === 'small',
'min-w-6 min-h-6': size === 'medium',
'min-w-20 min-h-20 !text-3xl': size === 'large',
'min-w-26 min-h-26 !text-4xl': size === 'xlarge',
}"
:style="{ backgroundColor }"
>
<template v-if="!props.hideLabel">
{{ usernameInitials }}
</template>
</div>
</template>

1
packages/nc-gui/composables/useGlobal/actions.ts

@ -38,6 +38,7 @@ export function useGlobalActions(state: State): Actions {
firstname: state.jwtPayload.value.firstname,
lastname: state.jwtPayload.value.lastname,
roles: state.jwtPayload.value.roles,
display_name: state.jwtPayload.value.display_name,
}
}
}

1
packages/nc-gui/composables/useGlobal/index.ts

@ -71,6 +71,7 @@ export const useGlobal = createGlobalState((): UseGlobalReturn => {
firstname: nextPayload.firstname,
lastname: nextPayload.lastname,
roles: nextPayload.roles,
display_name: nextPayload.display_name,
}
}
},

1
packages/nc-gui/lang/en.json

@ -289,6 +289,7 @@
"createdOn": "Created On",
"notifyVia": "Notify Via",
"projName": "Project name",
"profile": "Profile",
"tableName": "Table name",
"dashboardName": "Dashboard name",
"viewName": "View name",

1
packages/nc-gui/lib/types.ts

@ -24,6 +24,7 @@ interface User {
workspace_roles: Roles | string
invite_token?: string
project_id?: string
display_name?: string | null
}
interface ProjectMetaInfo {

65
packages/nc-gui/store/users.ts

@ -0,0 +1,65 @@
import { acceptHMRUpdate, defineStore } from 'pinia'
export const useUsers = defineStore('userStore', () => {
const { api } = useApi()
const { user } = useGlobal()
const currentUser = computed({
get: () => user.value,
set: (value) => {
user.value = value
},
})
const updateUserProfile = async ({
attrs,
}: {
attrs: {
display_name?: string
}
}) => {
if (!user.value) throw new Error('User is not defined')
await api.userProfile.update(attrs)
user.value = {
...user.value,
...attrs,
}
}
const loadCurrentUser = async () => {
const res = await api.auth.me()
user.value = {
...user.value,
...res,
roles: res.roles,
project_roles: res.project_roles,
workspace_roles: res.workspace_roles,
}
}
watch(
() => user.value?.id,
(newId, oldId) => {
if (!newId) return
if (newId === oldId) return
loadCurrentUser()
},
{
immediate: true,
},
)
return {
loadCurrentUser,
updateUserProfile,
currentUser,
}
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useUsers as any, import.meta.hot))
}
Loading…
Cancel
Save