Merge branch 'user-app-list' of github.com:Budibase/budibase into user-app-list

This commit is contained in:
mike12345567 2021-05-19 15:10:24 +01:00
commit dbf8e426bc
19 changed files with 123 additions and 103 deletions

View File

@ -7,8 +7,7 @@
Body, Body,
Heading, Heading,
} from "@budibase/bbui" } from "@budibase/bbui"
import { organisation } from "stores/portal" import { organisation, auth } from "stores/portal"
import { auth } from "stores/backend"
let email = "" let email = ""

View File

@ -11,8 +11,7 @@
Heading, Heading,
} from "@budibase/bbui" } from "@budibase/bbui"
import GoogleButton from "./GoogleButton.svelte" import GoogleButton from "./GoogleButton.svelte"
import { auth } from "stores/backend" import { organisation, auth } from "stores/portal"
import { organisation } from "stores/portal"
let username = "" let username = ""
let password = "" let password = ""

View File

@ -1,9 +1,8 @@
<script> <script>
import { notifications, Button, Layout, Body, Heading } from "@budibase/bbui" import { notifications, Button, Layout, Body, Heading } from "@budibase/bbui"
import { organisation } from "stores/portal" import { organisation, auth } from "stores/portal"
import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte" import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte"
import { params, goto } from "@roxi/routify" import { params, goto } from "@roxi/routify"
import { auth } from "stores/backend"
const resetCode = $params["?code"] const resetCode = $params["?code"]
let password, error let password, error

View File

@ -6,12 +6,9 @@
Layout, Layout,
ActionMenu, ActionMenu,
MenuItem, MenuItem,
Link,
} from "@budibase/bbui" } from "@budibase/bbui"
import { gradient } from "actions" import { gradient } from "actions"
import { AppStatus } from "constants" import { auth } from "stores/portal"
import { url } from "@roxi/routify"
import { auth } from "stores/backend"
export let app export let app
export let exportApp export let exportApp

View File

@ -1,7 +1,7 @@
<script> <script>
import { gradient } from "actions" import { gradient } from "actions"
import { Heading, Button, Icon, ActionMenu, MenuItem } from "@budibase/bbui" import { Heading, Button, Icon, ActionMenu, MenuItem } from "@budibase/bbui"
import { auth } from "stores/backend" import { auth } from "stores/portal"
export let app export let app
export let openApp export let openApp

View File

@ -1,8 +1,7 @@
<script> <script>
import { onMount } from "svelte" import { onMount } from "svelte"
import { isActive, redirect } from "@roxi/routify" import { isActive, redirect } from "@roxi/routify"
import { auth } from "stores/backend" import { admin, auth } from "stores/portal"
import { admin } from "stores/portal"
let loaded = false let loaded = false
$: hasAdminUser = !!$admin?.checklist?.adminUser $: hasAdminUser = !!$admin?.checklist?.adminUser

View File

@ -1,5 +1,5 @@
<script> <script>
import { auth } from "stores/backend" import { auth } from "stores/portal"
</script> </script>
{#if $auth.user} {#if $auth.user}

View File

@ -0,0 +1,27 @@
<script>
import { ModalContent, Body, notifications } from "@budibase/bbui"
import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte"
import { auth } from "stores/portal"
let password
let error
const updatePassword = async () => {
try {
await auth.updateSelf({ ...$auth.user, password })
notifications.success("Information updated successfully")
} catch (error) {
notifications.error("Failed to update password")
}
}
</script>
<ModalContent
title="Change password"
confirmText="Update password"
onConfirm={updatePassword}
disabled={error || !password}
>
<Body size="S">Enter your new password below.</Body>
<PasswordRepeatInput bind:password bind:error />
</ModalContent>

View File

@ -1,9 +1,7 @@
<script> <script>
import { ModalContent, Body, Input, notifications } from "@budibase/bbui" import { ModalContent, Body, Input, notifications } from "@budibase/bbui"
import { writable } from "svelte/store" import { writable } from "svelte/store"
import { auth } from "stores/backend" import { auth } from "stores/portal"
import { saveRow } from "components/backend/DataTable/api"
import { TableNames } from "constants"
const values = writable({ const values = writable({
firstName: $auth.user.firstName, firstName: $auth.user.firstName,
@ -11,17 +9,10 @@
}) })
const updateInfo = async () => { const updateInfo = async () => {
const newUser = { try {
...$auth.user, await auth.updateSelf({ ...$auth.user, ...$values })
firstName: $values.firstName,
lastName: $values.lastName,
}
console.log(newUser)
const response = await saveRow(newUser, TableNames.USERS)
if (response.ok) {
await auth.checkAuth()
notifications.success("Information updated successfully") notifications.success("Information updated successfully")
} else { } catch (error) {
notifications.error("Failed to update information") notifications.error("Failed to update information")
} }
} }

View File

@ -1,5 +1,5 @@
<script> <script>
import { auth } from "stores/backend" import { auth } from "stores/portal"
</script> </script>
{#if $auth.user} {#if $auth.user}

View File

@ -13,16 +13,16 @@
Modal, Modal,
} from "@budibase/bbui" } from "@budibase/bbui"
import { onMount } from "svelte" import { onMount } from "svelte"
import { apps, organisation } from "stores/portal" import { apps, organisation, auth } from "stores/portal"
import { auth } from "stores/backend"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { AppStatus } from "constants" import { AppStatus } from "constants"
import { gradient } from "actions" import { gradient } from "actions"
import UpdateUserInfoModal from "./_components/UpdateUserInfoModal.svelte" import UpdateUserInfoModal from "./_components/UpdateUserInfoModal.svelte"
import ChangePasswordModal from "./_components/ChangePasswordModal.svelte"
let loaded = false let loaded = false
let userInfoModal let userInfoModal
let userPasswordModal let changePasswordModal
onMount(async () => { onMount(async () => {
await organisation.init() await organisation.init()
@ -44,8 +44,7 @@
</Heading> </Heading>
<Body> <Body>
Welcome to the {$organisation.company} portal. Below you'll find Welcome to the {$organisation.company} portal. Below you'll find
the list of apps that you have access to, as well as company news the list of apps that you have access to.
and the employee handbook.
</Body> </Body>
</Layout> </Layout>
<ActionMenu align="right"> <ActionMenu align="right">
@ -56,7 +55,12 @@
<MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}> <MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}>
Update user information Update user information
</MenuItem> </MenuItem>
<MenuItem icon="LockClosed">Update password</MenuItem> <MenuItem
icon="LockClosed"
on:click={() => changePasswordModal.show()}
>
Update password
</MenuItem>
<MenuItem <MenuItem
icon="UserDeveloper" icon="UserDeveloper"
on:click={() => $goto("../portal")} on:click={() => $goto("../portal")}
@ -67,18 +71,10 @@
</ActionMenu> </ActionMenu>
</div> </div>
<Divider /> <Divider />
<div class="apps-title"> {#if $apps.length}
<Heading>Apps</Heading> <Heading>Apps</Heading>
<Select placeholder="Filter by groups" />
</div>
<div class="group"> <div class="group">
<Layout gap="S" noPadding> <Layout gap="S" noPadding>
<div class="group-title">
<Body weight="500" size="XS">GROUP</Body>
{#if $auth.user?.builder?.global}
<Icon name="Settings" hoverable />
{/if}
</div>
{#each $apps as app, idx (app.appId)} {#each $apps as app, idx (app.appId)}
<a class="app" target="_blank" href={`/${app.appId}`}> <a class="app" target="_blank" href={`/${app.appId}`}>
<div class="preview" use:gradient={{ seed: app.name }} /> <div class="preview" use:gradient={{ seed: app.name }} />
@ -93,6 +89,14 @@
{/each} {/each}
</Layout> </Layout>
</div> </div>
{:else}
<Layout gap="XS" noPadding>
<Heading size="S">You don't have access to any apps yet.</Heading>
<Body size="S">
The apps you have access to will be listed here.
</Body>
</Layout>
{/if}
</Layout> </Layout>
</div> </div>
</Page> </Page>
@ -100,6 +104,9 @@
<Modal bind:this={userInfoModal}> <Modal bind:this={userInfoModal}>
<UpdateUserInfoModal /> <UpdateUserInfoModal />
</Modal> </Modal>
<Modal bind:this={changePasswordModal}>
<ChangePasswordModal />
</Modal>
{/if} {/if}
<style> <style>
@ -130,19 +137,8 @@
cursor: pointer; cursor: pointer;
filter: brightness(110%); filter: brightness(110%);
} }
.apps-title {
display: grid;
grid-template-columns: 1fr 150px;
grid-gap: var(--spacing-xl);
}
.group { .group {
margin-top: 20px; margin-top: var(--spacing-s);
}
.group-title {
display: grid;
grid-template-columns: 1fr auto;
grid-gap: var(--spacing-xl);
align-items: center;
} }
.app { .app {
display: grid; display: grid;

View File

@ -1,6 +1,6 @@
<script> <script>
import { redirect } from "@roxi/routify" import { redirect } from "@roxi/routify"
import { auth } from "stores/backend" import { auth } from "stores/portal"
$: { $: {
if (!$auth.user) { if (!$auth.user) {

View File

@ -12,8 +12,7 @@
Modal, Modal,
} from "@budibase/bbui" } from "@budibase/bbui"
import ConfigChecklist from "components/common/ConfigChecklist.svelte" import ConfigChecklist from "components/common/ConfigChecklist.svelte"
import { organisation } from "stores/portal" import { organisation, auth } from "stores/portal"
import { auth } from "stores/backend"
import BuilderSettingsModal from "components/start/BuilderSettingsModal.svelte" import BuilderSettingsModal from "components/start/BuilderSettingsModal.svelte"
import { onMount } from "svelte" import { onMount } from "svelte"

View File

@ -17,8 +17,7 @@
import api, { del } from "builderStore/api" import api, { del } from "builderStore/api"
import analytics from "analytics" import analytics from "analytics"
import { onMount } from "svelte" import { onMount } from "svelte"
import { apps } from "stores/portal" import { apps, auth } from "stores/portal"
import { auth } from "stores/backend"
import download from "downloadjs" import download from "downloadjs"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"

View File

@ -14,45 +14,52 @@
import { organisation } from "stores/portal" import { organisation } from "stores/portal"
import { post } from "builderStore/api" import { post } from "builderStore/api"
import analytics from "analytics" import analytics from "analytics"
let analyticsDisabled = analytics.disabled() import { writable } from "svelte/store"
function toggleAnalytics() { const values = writable({
if (analyticsDisabled) { analytics: !analytics.disabled(),
analytics.optIn() company: $organisation.company,
} else { platformUrl: $organisation.platformUrl,
analytics.optOut() logo: $organisation.logoUrl
}
}
let loading = false
let file = $organisation.logoUrl
? { url: $organisation.logoUrl, type: "image", name: "Logo" } ? { url: $organisation.logoUrl, type: "image", name: "Logo" }
: null : null,
})
let loading = false
async function uploadLogo() { async function uploadLogo(file) {
let data = new FormData() let data = new FormData()
data.append("file", file) data.append("file", file)
const res = await post("/api/admin/configs/upload/settings/logo", data, {}) const res = await post("/api/admin/configs/upload/settings/logo", data, {})
return await res.json() return await res.json()
} }
async function saveConfig() { async function saveConfig() {
loading = true loading = true
await toggleAnalytics()
if (file) { // Set analytics preference
await uploadLogo() if ($values.analytics) {
analytics.optIn()
} else {
analytics.optOut()
}
// Upload logo if required
if ($values.logo && !$values.logo.url) {
await uploadLogo($values.logo)
await organisation.init() await organisation.init()
} }
// Update settings
const res = await organisation.save({ const res = await organisation.save({
company: $organisation.company, company: $values.company ?? "",
platformUrl: $organisation.platformUrl, platformUrl: $values.platformUrl ?? "",
}) })
if (res.status === 200) { if (res.status === 200) {
notifications.success("Settings saved.") notifications.success("Settings saved successfully")
} else { } else {
notifications.error(res.message) notifications.error(res.message)
} }
loading = false loading = false
} }
</script> </script>
@ -73,15 +80,15 @@
<div class="fields"> <div class="fields">
<div class="field"> <div class="field">
<Label size="L">Organization name</Label> <Label size="L">Organization name</Label>
<Input thin bind:value={$organisation.company} /> <Input thin bind:value={$values.company} />
</div> </div>
<div class="field logo"> <div class="field logo">
<Label size="L">Logo</Label> <Label size="L">Logo</Label>
<div class="file"> <div class="file">
<Dropzone <Dropzone
value={[file]} value={[$values.logo]}
on:change={e => { on:change={e => {
file = e.detail?.[0] $values.logo = e.detail?.[0]
}} }}
/> />
</div> </div>
@ -95,7 +102,7 @@
<div class="fields"> <div class="fields">
<div class="field"> <div class="field">
<Label size="L">Platform URL</Label> <Label size="L">Platform URL</Label>
<Input thin bind:value={$organisation.platformUrl} /> <Input thin bind:value={$values.platformUrl} />
</div> </div>
</div> </div>
<Divider size="S" /> <Divider size="S" />
@ -110,7 +117,7 @@
<div class="fields"> <div class="fields">
<div class="field"> <div class="field">
<Label size="L">Send Analytics to Budibase</Label> <Label size="L">Send Analytics to Budibase</Label>
<Toggle text="" value={!analyticsDisabled} /> <Toggle text="" bind:value={$values.analytics} />
</div> </div>
</div> </div>
</Layout> </Layout>

View File

@ -7,4 +7,3 @@ export { roles } from "./roles"
export { datasources } from "./datasources" export { datasources } from "./datasources"
export { integrations } from "./integrations" export { integrations } from "./integrations"
export { queries } from "./queries" export { queries } from "./queries"
export { auth } from "./auth"

View File

@ -7,7 +7,7 @@ export function createAuthStore() {
return { return {
subscribe: store.subscribe, subscribe: store.subscribe,
checkAuth: async () => { checkAuth: async () => {
const response = await api.get("/api/self") const response = await api.get("/api/admin/users/self")
const user = await response.json() const user = await response.json()
if (response.status === 200) { if (response.status === 200) {
store.update(state => ({ ...state, user })) store.update(state => ({ ...state, user }))
@ -33,6 +33,14 @@ export function createAuthStore() {
await response.json() await response.json()
store.update(state => ({ ...state, user: null })) store.update(state => ({ ...state, user: null }))
}, },
updateSelf: async user => {
const response = await api.post("/api/admin/users/self", user)
if (response.status === 200) {
store.update(state => ({ ...state, user: { ...state.user, ...user } }))
} else {
throw "Unable to update user details"
}
},
forgotPassword: async email => { forgotPassword: async email => {
const response = await api.post(`/api/admin/auth/reset`, { const response = await api.post(`/api/admin/auth/reset`, {
email, email,

View File

@ -3,3 +3,4 @@ export { users } from "./users"
export { admin } from "./admin" export { admin } from "./admin"
export { apps } from "./apps" export { apps } from "./apps"
export { email } from "./email" export { email } from "./email"
export { auth } from "./auth"

View File

@ -2,7 +2,7 @@ import { writable, get } from "svelte/store"
import api from "builderStore/api" import api from "builderStore/api"
const DEFAULT_CONFIG = { const DEFAULT_CONFIG = {
platformUrl: undefined, platformUrl: "http://localhost:1000",
logoUrl: "https://i.imgur.com/ZKyklgF.png", logoUrl: "https://i.imgur.com/ZKyklgF.png",
docsUrl: undefined, docsUrl: undefined,
company: "Budibase", company: "Budibase",