forked from tsai/budibase
14 changed files with 336 additions and 26 deletions
@ -0,0 +1,54 @@ |
|||||
|
<script> |
||||
|
import { isActive, url, goto } from "@roxi/routify" |
||||
|
import { onMount } from "svelte" |
||||
|
import { |
||||
|
ActionMenu, |
||||
|
Checkbox, |
||||
|
Body, |
||||
|
MenuItem, |
||||
|
Icon, |
||||
|
Heading, |
||||
|
Avatar, |
||||
|
Search, |
||||
|
Layout, |
||||
|
ProgressCircle, |
||||
|
SideNavigation as Navigation, |
||||
|
SideNavigationItem as Item, |
||||
|
} from "@budibase/bbui" |
||||
|
import api from "builderStore/api" |
||||
|
import { organisation, admin } from "stores/portal" |
||||
|
|
||||
|
const MESSAGES = { |
||||
|
apps: "Create your first app", |
||||
|
smtp: "Set up email", |
||||
|
adminUser: "Create your first user", |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<ActionMenu> |
||||
|
<div slot="control" class="icon"> |
||||
|
<ProgressCircle size="S" value={$admin.onboardingProgress} /> |
||||
|
</div> |
||||
|
<MenuItem disabled> |
||||
|
<header class="item"> |
||||
|
<Heading size="XXS">Get Started Checklist</Heading> |
||||
|
<ProgressCircle size="S" value={$admin.onboardingProgress} /> |
||||
|
</header> |
||||
|
</MenuItem> |
||||
|
{#each Object.keys($admin.checklist) as checklistItem, idx} |
||||
|
<MenuItem> |
||||
|
<div class="item"> |
||||
|
<span>{idx + 1}. {MESSAGES[checklistItem]}</span> |
||||
|
<Checkbox value={!!$admin.checklist[checklistItem]} /> |
||||
|
</div> |
||||
|
</MenuItem> |
||||
|
{/each} |
||||
|
</ActionMenu> |
||||
|
|
||||
|
<style> |
||||
|
.item { |
||||
|
display: grid; |
||||
|
align-items: center; |
||||
|
grid-template-columns: 200px 20px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,29 @@ |
|||||
|
<script> |
||||
|
import { onMount } from "svelte" |
||||
|
import { goto } from "@roxi/routify" |
||||
|
import { |
||||
|
SideNavigation as Navigation, |
||||
|
SideNavigationItem as Item, |
||||
|
} from "@budibase/bbui" |
||||
|
import { admin } from "stores/portal" |
||||
|
import LoginForm from "components/login/LoginForm.svelte" |
||||
|
import BuilderSettingsButton from "components/start/BuilderSettingsButton.svelte" |
||||
|
import LogoutButton from "components/start/LogoutButton.svelte" |
||||
|
import Logo from "/assets/budibase-logo.svg" |
||||
|
import api from "builderStore/api" |
||||
|
|
||||
|
let checklist |
||||
|
|
||||
|
onMount(async () => { |
||||
|
await admin.init() |
||||
|
if (!$admin?.checklist?.adminUser) { |
||||
|
$goto("./admin") |
||||
|
} else { |
||||
|
$goto("./portal") |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
{#if $admin.checklist} |
||||
|
<slot /> |
||||
|
{/if} |
||||
@ -0,0 +1,69 @@ |
|||||
|
<script> |
||||
|
import { |
||||
|
Button, |
||||
|
Heading, |
||||
|
Label, |
||||
|
notifications, |
||||
|
Layout, |
||||
|
Input, |
||||
|
Body, |
||||
|
} from "@budibase/bbui" |
||||
|
import { goto } from "@roxi/routify" |
||||
|
import { onMount } from "svelte" |
||||
|
import api from "builderStore/api" |
||||
|
|
||||
|
let adminUser = {} |
||||
|
|
||||
|
async function save() { |
||||
|
try { |
||||
|
// Save the admin user |
||||
|
const response = await api.post(`/api/admin/users/init`, adminUser) |
||||
|
|
||||
|
const json = await response.json() |
||||
|
if (response.status !== 200) throw new Error(json.message) |
||||
|
notifications.success(`Admin user created.`) |
||||
|
$goto("../portal") |
||||
|
} catch (err) { |
||||
|
notifications.error(`Failed to create admin user.`) |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<section> |
||||
|
<div class="container"> |
||||
|
<header> |
||||
|
<Heading size="M">Create an admin user</Heading> |
||||
|
<Body size="S">The admin user has access to everything in budibase.</Body> |
||||
|
</header> |
||||
|
<div class="config-form"> |
||||
|
<Layout gap="S"> |
||||
|
<Input label="email" bind:value={adminUser.email} /> |
||||
|
<Input |
||||
|
label="password" |
||||
|
type="password" |
||||
|
bind:value={adminUser.password} |
||||
|
/> |
||||
|
<Button cta on:click={save}>Create super admin user</Button> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<style> |
||||
|
section { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
header { |
||||
|
text-align: center; |
||||
|
width: 80%; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
|
||||
|
.config-form { |
||||
|
margin-bottom: 42px; |
||||
|
} |
||||
|
</style> |
||||
@ -1,7 +1,36 @@ |
|||||
import { writable } from "svelte/store" |
import { writable } from "svelte/store" |
||||
|
import api from "builderStore/api" |
||||
|
|
||||
const INITIAL_ADMIN_STATE = { |
export function createAdminStore() { |
||||
oauth: [], |
const { subscribe, set } = writable({}) |
||||
|
|
||||
|
async function init() { |
||||
|
try { |
||||
|
const response = await api.get("/api/admin/configs/checklist") |
||||
|
const json = await response.json() |
||||
|
|
||||
|
const onboardingSteps = Object.keys(json) |
||||
|
|
||||
|
const stepsComplete = onboardingSteps.reduce( |
||||
|
(score, step) => score + Number(!!json[step]), |
||||
|
0 |
||||
|
) |
||||
|
|
||||
|
set({ |
||||
|
checklist: json, |
||||
|
onboardingProgress: (stepsComplete / onboardingSteps.length) * 100, |
||||
|
}) |
||||
|
} catch (err) { |
||||
|
set({ |
||||
|
checklist: null, |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
subscribe, |
||||
|
init, |
||||
|
} |
||||
} |
} |
||||
|
|
||||
export const admin = writable({ ...INITIAL_ADMIN_STATE }) |
export const admin = createAdminStore() |
||||
|
|||||
@ -0,0 +1,38 @@ |
|||||
|
const setup = require("./utilities") |
||||
|
|
||||
|
// mock the email system
|
||||
|
const sendMailMock = jest.fn() |
||||
|
jest.mock("nodemailer") |
||||
|
const nodemailer = require("nodemailer") |
||||
|
nodemailer.createTransport.mockReturnValue({ |
||||
|
verify: jest.fn() |
||||
|
}) |
||||
|
|
||||
|
describe("/api/admin/configs/checklist", () => { |
||||
|
let request = setup.getRequest() |
||||
|
let config = setup.getConfig() |
||||
|
|
||||
|
beforeAll(async () => { |
||||
|
await config.init() |
||||
|
}) |
||||
|
|
||||
|
afterAll(setup.afterAll) |
||||
|
|
||||
|
it("should return the correct checklist status based on the state of the budibase installation", async () => { |
||||
|
// initially configure settings
|
||||
|
await config.saveAdminUser() |
||||
|
await config.saveSmtpConfig() |
||||
|
|
||||
|
const res = await request |
||||
|
.get(`/api/admin/configs/checklist`) |
||||
|
.set(config.defaultHeaders()) |
||||
|
.expect("Content-Type", /json/) |
||||
|
.expect(200) |
||||
|
|
||||
|
const checklist = res.body |
||||
|
|
||||
|
expect(checklist.apps).toBe(0) |
||||
|
expect(checklist.smtp).toBe(true) |
||||
|
expect(checklist.adminUser).toBe(true) |
||||
|
}) |
||||
|
}) |
||||
Loading…
Reference in new issue