forked from tsai/budibase
18 changed files with 414 additions and 49 deletions
@ -1,12 +1,58 @@ |
|||
<script> |
|||
import "@spectrum-css/avatar/dist/index-vars.css" |
|||
let sizes = new Map([ |
|||
["XXS", "--spectrum-alias-avatar-size-50"], |
|||
["XS", "--spectrum-alias-avatar-size-75"], |
|||
["S", "--spectrum-alias-avatar-size-200"], |
|||
["M", "--spectrum-alias-avatar-size-300"], |
|||
["L", "--spectrum-alias-avatar-size-500"], |
|||
["XL", "--spectrum-alias-avatar-size-600"], |
|||
["XXL", "--spectrum-alias-avatar-size-700"], |
|||
]) |
|||
export let size = "M" |
|||
export let url = "" |
|||
export let disabled = false |
|||
export let name = "John Doe" |
|||
|
|||
function getInitials(name) { |
|||
let parts = name.split(" ") |
|||
return parts.map((name) => name[0]).join("") |
|||
} |
|||
</script> |
|||
|
|||
<img |
|||
class:is-disabled={disabled} |
|||
class="spectrum-Avatar" |
|||
src={url} |
|||
alt="Avatar" |
|||
/> |
|||
{#if url} |
|||
<img |
|||
class:is-disabled={disabled} |
|||
class="spectrum-Avatar" |
|||
src={url} |
|||
alt="Avatar" |
|||
style="width: var({sizes.get(size)}); height: var({sizes.get(size)});" |
|||
/> |
|||
{:else} |
|||
<div |
|||
class:is-disabled={disabled} |
|||
style="width: var({sizes.get(size)}); height: var({sizes.get( |
|||
size |
|||
)}); font-size: calc(var({sizes.get(size)}) / 2)" |
|||
> |
|||
{getInitials(name)} |
|||
</div> |
|||
{/if} |
|||
|
|||
<style> |
|||
div { |
|||
color: white; |
|||
display: grid; |
|||
place-items: center; |
|||
font-weight: 500; |
|||
background: rgb(63, 94, 251); |
|||
background: linear-gradient( |
|||
155deg, |
|||
rgba(63, 94, 251, 1) 0%, |
|||
rgba(53, 199, 86, 1) 47% |
|||
); |
|||
border-radius: 50%; |
|||
overflow: hidden; |
|||
user-select: none; |
|||
} |
|||
</style> |
|||
|
|||
@ -0,0 +1,11 @@ |
|||
import { writable } from "svelte/store" |
|||
|
|||
const INITIAL_ADMIN_STATE = { |
|||
oauth: [], |
|||
} |
|||
|
|||
export const getAdminStore = () => { |
|||
const store = writable({ ...INITIAL_ADMIN_STATE }) |
|||
store.actions = {} |
|||
return store |
|||
} |
|||
@ -0,0 +1 @@ |
|||
Index route |
|||
@ -0,0 +1,126 @@ |
|||
<script> |
|||
import { isActive, url } from "@roxi/routify" |
|||
import { onMount } from "svelte" |
|||
import { |
|||
Icon, |
|||
Avatar, |
|||
Search, |
|||
Layout, |
|||
ProgressCircle, |
|||
SideNavigation as Navigation, |
|||
SideNavigationItem as Item, |
|||
} from "@budibase/bbui" |
|||
|
|||
let orgName, orgLogo, onBoardingProgress, user |
|||
|
|||
async function getInfo() { |
|||
// fetch orgInfo |
|||
orgName = "ACME Inc." |
|||
orgLogo = "https://via.placeholder.com/150" |
|||
|
|||
// set onBoardingProgress |
|||
onBoardingProgress = 20 |
|||
user = { name: "John Doe" } |
|||
} |
|||
|
|||
onMount(getInfo) |
|||
|
|||
let menu = [ |
|||
{ title: "Apps", href: "/portal" }, |
|||
{ title: "Drafts", href: "/portal/drafts" }, |
|||
{ title: "Users", href: "/portal/users", heading: "Manage" }, |
|||
{ title: "Groups", href: "/portal/groups" }, |
|||
{ title: "Auth", href: "/portal/oauth" }, |
|||
{ title: "Email", href: "/portal/email" }, |
|||
{ title: "General", href: "/portal/general", heading: "Settings" }, |
|||
{ title: "Theming", href: "/portal/theming" }, |
|||
{ title: "Account", href: "/portal/account" }, |
|||
] |
|||
</script> |
|||
|
|||
<div class="container"> |
|||
<Layout> |
|||
<div class="nav"> |
|||
<div class="branding"> |
|||
<div class="name"> |
|||
<img src={orgLogo} alt="Logotype" /> |
|||
<span>{orgName}</span> |
|||
</div> |
|||
<div class="onboarding"> |
|||
<ProgressCircle size="S" value={onBoardingProgress} /> |
|||
</div> |
|||
</div> |
|||
<div class="menu"> |
|||
<Navigation> |
|||
{#each menu as { title, href, heading }} |
|||
<Item selected={$isActive(href)} href={$url(href)} {heading}> |
|||
{title} |
|||
</Item> |
|||
{/each} |
|||
</Navigation> |
|||
</div> |
|||
</div> |
|||
</Layout> |
|||
<div class="main"> |
|||
<div class="toolbar"> |
|||
<Search /> |
|||
<div class="avatar"> |
|||
<Avatar size="M" name="John Doe" /> |
|||
<Icon size="XL" name="ChevronDown" /> |
|||
</div> |
|||
</div> |
|||
<div class="content"> |
|||
<slot /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
.container { |
|||
min-height: 100vh; |
|||
display: grid; |
|||
grid-template-columns: 250px 1fr; |
|||
} |
|||
.main { |
|||
display: grid; |
|||
grid-template-rows: auto 1fr; |
|||
border-left: 2px solid var(--spectrum-alias-background-color-primary); |
|||
} |
|||
.branding { |
|||
display: grid; |
|||
grid-template-columns: auto auto; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: var(--spacing-xl); |
|||
} |
|||
.name { |
|||
display: grid; |
|||
grid-template-columns: auto auto; |
|||
grid-gap: var(--spacing-m); |
|||
align-items: center; |
|||
} |
|||
.content { |
|||
padding: var(--spacing-m); |
|||
} |
|||
.avatar { |
|||
display: grid; |
|||
grid-template-columns: auto auto; |
|||
place-items: center; |
|||
grid-gap: var(--spacing-xs); |
|||
} |
|||
.avatar:hover { |
|||
cursor: pointer; |
|||
filter: brightness(110%); |
|||
} |
|||
.toolbar { |
|||
border-bottom: 2px solid var(--spectrum-alias-background-color-primary); |
|||
display: grid; |
|||
grid-template-columns: 250px auto; |
|||
justify-content: space-between; |
|||
padding: var(--spacing-m) calc(var(--spacing-xl) * 2); |
|||
} |
|||
img { |
|||
width: 32px; |
|||
height: 32px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,118 @@ |
|||
<script> |
|||
import GoogleLogo from "./logos/Google.svelte" |
|||
import { |
|||
Button, |
|||
Heading, |
|||
Divider, |
|||
Label, |
|||
notifications, |
|||
Layout, |
|||
Input, |
|||
Body, |
|||
} from "@budibase/bbui" |
|||
import { onMount } from "svelte" |
|||
import api from "builderStore/api" |
|||
|
|||
const ConfigTypes = { |
|||
Google: "google", |
|||
// Github: "github", |
|||
// AzureAD: "ad", |
|||
} |
|||
|
|||
const ConfigFields = { |
|||
Google: ["clientID", "clientSecret", "callbackURL"], |
|||
} |
|||
|
|||
let google |
|||
|
|||
async function save(doc) { |
|||
try { |
|||
// Save an oauth config |
|||
const response = await api.post(`/api/admin/configs`, doc) |
|||
const json = await response.json() |
|||
if (response.status !== 200) throw new Error(json.message) |
|||
google._rev = json._rev |
|||
google._id = json._id |
|||
|
|||
notifications.success(`Settings saved.`) |
|||
} catch (err) { |
|||
notifications.error(`Failed to update OAuth settings. ${err}`) |
|||
} |
|||
} |
|||
|
|||
onMount(async () => { |
|||
// fetch the configs for oauth |
|||
const googleResponse = await api.get( |
|||
`/api/admin/configs/${ConfigTypes.Google}` |
|||
) |
|||
const googleDoc = await googleResponse.json() |
|||
|
|||
if (!googleDoc._id) { |
|||
google = { |
|||
type: ConfigTypes.Google, |
|||
config: {}, |
|||
} |
|||
} else { |
|||
google = googleDoc |
|||
} |
|||
}) |
|||
</script> |
|||
|
|||
<section class="container"> |
|||
<header> |
|||
<Heading size="M">OAuth</Heading> |
|||
<Body size="S"> |
|||
Every budibase app comes with basic authentication (email/password) |
|||
included. You can add additional authentication methods from the options |
|||
below. |
|||
</Body> |
|||
</header> |
|||
<Divider /> |
|||
{#if google} |
|||
<div class="config-form"> |
|||
<Layout gap="S"> |
|||
<Heading size="S"> |
|||
<span> |
|||
<GoogleLogo /> |
|||
Google |
|||
</span> |
|||
</Heading> |
|||
{#each ConfigFields.Google as field} |
|||
<div class="form-row"> |
|||
<Label>{field}</Label> |
|||
<Input bind:value={google.config[field]} /> |
|||
</div> |
|||
{/each} |
|||
</Layout> |
|||
<Button primary on:click={() => save(google)}>Save</Button> |
|||
</div> |
|||
<Divider /> |
|||
{/if} |
|||
</section> |
|||
|
|||
<style> |
|||
section { |
|||
margin: 60px 320px; |
|||
} |
|||
|
|||
.config-form { |
|||
margin-top: 42px; |
|||
margin-bottom: 42px; |
|||
} |
|||
|
|||
.form-row { |
|||
display: grid; |
|||
grid-template-columns: 20% 1fr; |
|||
grid-gap: var(--spacing-l); |
|||
} |
|||
|
|||
span { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: var(--spacing-s); |
|||
} |
|||
|
|||
header { |
|||
margin-bottom: 42px; |
|||
} |
|||
</style> |
|||
|
After Width: | Height: | Size: 1.5 KiB |
Loading…
Reference in new issue