Merge branch 'user-app-list' of github.com:Budibase/budibase into user-app-list
This commit is contained in:
commit
dbf8e426bc
|
@ -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 = ""
|
||||||
|
|
||||||
|
|
|
@ -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 = ""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { auth } from "stores/backend"
|
import { auth } from "stores/portal"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $auth.user}
|
{#if $auth.user}
|
||||||
|
|
|
@ -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>
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { auth } from "stores/backend"
|
import { auth } from "stores/portal"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $auth.user}
|
{#if $auth.user}
|
||||||
|
|
|
@ -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,32 +71,32 @@
|
||||||
</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 class="group">
|
||||||
</div>
|
<Layout gap="S" noPadding>
|
||||||
<div class="group">
|
{#each $apps as app, idx (app.appId)}
|
||||||
<Layout gap="S" noPadding>
|
<a class="app" target="_blank" href={`/${app.appId}`}>
|
||||||
<div class="group-title">
|
<div class="preview" use:gradient={{ seed: app.name }} />
|
||||||
<Body weight="500" size="XS">GROUP</Body>
|
<div class="app-info">
|
||||||
{#if $auth.user?.builder?.global}
|
<Heading size="XS">{app.name}</Heading>
|
||||||
<Icon name="Settings" hoverable />
|
<Body size="S">
|
||||||
{/if}
|
Edited {Math.round(Math.random() * 10 + 1)} months ago
|
||||||
</div>
|
</Body>
|
||||||
{#each $apps as app, idx (app.appId)}
|
</div>
|
||||||
<a class="app" target="_blank" href={`/${app.appId}`}>
|
<Icon name="ChevronRight" />
|
||||||
<div class="preview" use:gradient={{ seed: app.name }} />
|
</a>
|
||||||
<div class="app-info">
|
{/each}
|
||||||
<Heading size="XS">{app.name}</Heading>
|
</Layout>
|
||||||
<Body size="S">
|
</div>
|
||||||
Edited {Math.round(Math.random() * 10 + 1)} months ago
|
{:else}
|
||||||
</Body>
|
<Layout gap="XS" noPadding>
|
||||||
</div>
|
<Heading size="S">You don't have access to any apps yet.</Heading>
|
||||||
<Icon name="ChevronRight" />
|
<Body size="S">
|
||||||
</a>
|
The apps you have access to will be listed here.
|
||||||
{/each}
|
</Body>
|
||||||
</Layout>
|
</Layout>
|
||||||
</div>
|
{/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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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() {
|
|
||||||
if (analyticsDisabled) {
|
|
||||||
analytics.optIn()
|
|
||||||
} else {
|
|
||||||
analytics.optOut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const values = writable({
|
||||||
|
analytics: !analytics.disabled(),
|
||||||
|
company: $organisation.company,
|
||||||
|
platformUrl: $organisation.platformUrl,
|
||||||
|
logo: $organisation.logoUrl
|
||||||
|
? { url: $organisation.logoUrl, type: "image", name: "Logo" }
|
||||||
|
: null,
|
||||||
|
})
|
||||||
let loading = false
|
let loading = false
|
||||||
let file = $organisation.logoUrl
|
|
||||||
? { url: $organisation.logoUrl, type: "image", name: "Logo" }
|
|
||||||
: null
|
|
||||||
|
|
||||||
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>
|
||||||
|
|
|
@ -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"
|
|
||||||
|
|
|
@ -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,
|
|
@ -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"
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue